简单来说,this
是一个在函数运行时自动生成的特殊变量,它指向函数的“当前所有者”。这个“所有者”会根据函数的调用方式不同而变化。这就是 this
让人困惑的根本原因:它不是固定不变的,而是动态决定的。
为了理解 this
,我们可以从第一性原理出发:JavaScript 设计 this
的初衷是为了让函数能够根据上下文动态访问调用它的对象。 接下来,我们通过四种主要绑定规则来逐一拆解 this
的指向。
当一个函数被独立调用(不依赖任何对象)时,this
默认指向全局对象。在浏览器中,全局对象是 window
;在 Node.js 中则是 global
。来看个例子:
但如果开启了严格模式('use strict'
),情况就变了:
为什么会这样? 严格模式是为了避免意外修改全局对象,所以 this
在独立调用时被设置为 undefined
。这个规则提醒我们:独立调用的函数,this
** 默认指向全局对象,但在严格模式下要小心它是 undefined
。**
当函数作为对象的方法被调用时,this
指向调用该方法的对象。这是最常见的情况:
这里的 greet
是 user
对象的方法,调用时 this
指向 user
,所以能访问到 name
属性。
但要小心一个常见陷阱:如果把方法赋值给一个变量再调用,this
会丢失:
为什么会这样? 因为 sayHi()
是独立调用,不再依附于 user
对象,this
又回到了默认绑定的规则(全局对象或 undefined
)。解决办法 是后面会提到的 bind
或箭头函数。
JavaScript 提供了 call
、apply
和 bind
三种方法,让你明确指定 this
的值。这种方式非常灵活,适合需要动态改变 this
指向的场景:
call
和 apply
都会立即调用函数,区别在于参数传递方式:call
是一个个传,apply
是以数组形式传。bind
不会立即调用,而是返回一个新函数,固定了 this
的值。适用场景:当你需要临时改变 this
指向,或者想复用一个函数但让它在不同对象上工作时,显式绑定是你的好帮手。
当用 new
关键字调用构造函数时,this
指向新创建的实例对象:
为什么会这样? 使用 new
时,JavaScript 会创建一个新对象,并将构造函数中的 this
绑定到这个新对象上。这让构造函数可以初始化对象的属性。
箭头函数(=>
)是 ES6 引入的,它对 this
的处理方式完全不同:箭头函数没有自己的 this
,它会继承外层作用域的 this
值。
来看一个对比:
sayLater1
中,setTimeout
里的普通函数是独立调用的,this
指向全局对象(或 undefined
)。sayLater2
中,箭头函数继承了外层 sayLater2
的 this
,即 obj
,所以能正确访问 name
。关键点:箭头函数的 this
在定义时就固定了,不会因为调用方式改变。这让它特别适合用在回调函数中,避免 this
丢失的问题。
回调函数是 this
容易出问题的地方,比如:
这里 counter.increase
被作为回调函数传递,调用时失去了 counter
的上下文,this
变成了全局对象,导致 count
是 undefined
。
解决方案 有两种:
this
,可以直接访问 counter
:this
:在 DOM 事件处理中,普通函数的 this
指向触发事件的元素:
但如果用箭头函数,this
会指向外层作用域:
建议:如果需要访问触发事件的元素,用普通函数;如果需要外层作用域的 this
,用箭头函数。
面对复杂的代码,如何快速判断 this
指向哪里?可以用这个简单的方法:
new
绑定优先级最高,this
指向新对象。call
、apply
、bind
)。undefined
)。this
。this
的核心在于它是一个动态绑定的变量,取决于函数的调用方式。掌握了四种绑定规则(默认绑定、隐式绑定、显式绑定、new 绑定)和箭头函数的特性,你就能在大多数场景下游刃有余。遇到问题时,记得用 bind
或箭头函数来解决 this
丢失的麻烦。