先记住这个答案
普通函数直接调用(不是作为方法,也没有用 call 或 apply 指定接收者),在非严格模式下 this 会被自动绑定到全局对象。严格模式取消了这个自动绑定,this 就是 undefined。这样做的好处是让误操作立刻暴露:如果函数里写了 this.name = 'test',非严格模式会在全局对象上创建 name 属性,严格模式则会因为访问 undefined 的属性而抛 TypeError。
- 非严格模式普通调用时
this是全局对象 - 严格模式普通调用时
this是undefined - 方法调用或
bind绑定过的不受影响 - ESM 普通函数直调的
this未绑定
两种模式下 this 的对比
下面用一段代码直接看差异。
this 的值JavaScriptfunction showThis() {
'use strict'
return this
}
function showThisSloppy() {
return this
}
console.log(showThis() === undefined)
console.log(typeof showThisSloppy())查看输出与解释
true
objectshowThis 开了严格模式,直接调用返回 undefined。showThisSloppy 是非严格的,返回全局对象(typeof 结果是 object)。"use strict" 写在函数体内只影响这一个函数,不影响外面。
哪些调用方式不受影响
严格模式只改变了「没有明确调用者」时 this 的默认值。下面这些写法不受影响。
obj.fn() 这样的方法调用,this 是 obj。fn.call(obj) 和 fn.apply(obj) 手动指定了 this。fn.bind(obj) 返回的新函数永远绑定 obj。new fn() 里 this 指向新创建的实例。这些场景里 this 都有明确的来源,引擎不需要做「自动绑定到全局」这个兜底操作,所以严格不严格结果一样。
call 传 null 的区别JavaScriptfunction getThis() {
'use strict'
return this
}
function getThisSloppy() {
return this
}
console.log(getThis.call(null) === null)
console.log(typeof getThisSloppy.call(null))查看输出与解释
true
object用 call 传 null 或 undefined 时,非严格模式会把它替换成全局对象,严格模式原样保留。这也是同一条规则:非严格模式总想给 this 找一个对象,严格模式不做任何转换。
实际项目里的影响
现在写的代码大部分都是 ES 模块,ES 模块默认就是严格模式,所以函数里 this 为 undefined 是日常行为,不需要手动开启。
一个容易踩坑的场景是回调函数。把对象的方法传给 setTimeout,this 不再指向原来的对象。在非严格模式下它变成全局对象,好歹能跑通(虽然结果不对);在严格模式下它是 undefined,直接报错。这反而是好事,报错比静默出错更容易发现问题。解决方案是用箭头函数或 bind。
容易答错的地方
- 以为箭头函数的
this也会变成undefined - 箭头函数没有自己的
this,它用的是定义时外层作用域的this。如果外层是模块顶层,那确实是undefined;如果外层是一个方法调用,this就是那个对象。箭头函数的this和严格模式这条规则无关。 - 以为严格模式会影响方法调用
obj.fn()这种写法里,this始终是obj,不管严不严格。严格模式只改变「光秃秃调用一个函数」时的默认值,有明确调用者的情况不受影响。
面试官还会怎么问?
非严格模式下 this 为什么要指向全局对象?
这是 JavaScript 早期的设计。当时没有 let 和 const,全局变量和全局对象属性是一回事。函数里 this 指向全局对象让你可以用 this.setTimeout 这样的写法访问全局 API。现在看来这个设计弊大于利,所以严格模式把它去掉了。
class 里的方法是严格模式吗?
是的。class 内部整体都是严格模式,包括构造函数和所有方法。所以如果你把 class 的方法提取出来直接调用,this 就是 undefined。这也是 React 早期 class 组件需要在构造函数里 bind 方法的原因。
setTimeout 回调里的 this 是什么?
非严格模式下是全局对象,严格模式下是 undefined。setTimeout 内部相当于直接调用你传进去的函数,没有指定 this。如果需要保持 this,用箭头函数或者 bind。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。