在 JS 中, this
是一个关键概念,用于确定当前执行上下文中的上下文对象。this
的指向在不同的情况下会有所不同,这也是 JS 中最容易令人困惑的地方之一。
什么是 this?
this
是一个特殊关键字,用于引用当前执行上下文中的对象。它是一个非常重要的概念,因为它决定了在代码执行时,函数内部的上下文对象是什么。这个上下文对象可以是一个普通的 JS 对象,也可以是全局对象(如 window
在浏览器中),具体取决于函数的调用方式。
|
|
在浏览器中,this
的默认值是全局对象 window
。在 Node.js 中,this
的默认值是一个空对象 {}
(或者可以说 global
对象)。
this
在 JS 中有几种绑定规则,我们将在下面的章节中进行讨论。
默认绑定
默认绑定是指在独立函数调用时,this
的默认绑定规则。在这种情况下,this
的值是全局对象 window
。
|
|
为什么在这里我们会得到 window
对象呢?这是因为在这里,foo
函数是作为一个独立函数调用的,而不是作为对象的方法调用的。因此,this
的默认绑定规则会将 this
绑定到全局对象 window
上。
如果我们使用了 use strict
严格模式,那么在独立函数调用时,默认绑定的规则对全局对象 window
就不再生效了,此时 this
的值会是 undefined
。
|
|
隐式绑定
隐式绑定是指在对象方法调用时,this
的默认绑定规则。在这种情况下,this
的值是调用方法的对象。
|
|
注意,这里的函数 foo
被声明在全局作用域后作为引用添加到了 obj
的属性上,obj
拥有的只有对 foo
的引用,而不是 foo
本身。因此,当我们调用 obj.foo()
时,foo
函数的调用方式是作为对象的方法调用的,此时隐式绑定规则会将 this
绑定到 obj
上。
我们还可以考虑一点,如果我们将 obj
中的 foo
的引用赋值给另一个变量 bar
,那么 bar
也会拥有对 foo
的引用,此时我们调用 bar()
时,foo
函数的调用方式是作为独立函数调用的,此时默认绑定规则会将 this
绑定到全局对象 window
上。
|
|
如果你有一条引用链,那么隐式绑定规则会将 this
绑定到最后一层调用位置上。
|
|
隐式丢失
隐式绑定规则会在调用位置上绑定 this
,但是如果我们将函数从原始的对象中取出来,那么隐式绑定规则就会丢失,取而代之的是默认绑定规则。
|
|
显式绑定
显式绑定是指在函数调用时,使用 call
、apply
或者 bind
方法来指定 this
的绑定对象。在这种情况下,this
的值是显式指定的绑定对象。
|
|
硬绑定
硬绑定是指在函数调用时,使用 call
、apply
或者 bind
方法来指定 this
的绑定对象,从而强制绑定 this
的绑定对象。在这种情况下,this
的值是显式指定的绑定对象,而且无法被修改。
|
|
在这里,我们使用 call
方法来强制绑定 this
的绑定对象为 obj
,这样无论我们如何调用 bar
,this
的值都会被强制绑定为 obj
。
new 绑定
new
绑定是指在使用 new
关键字调用构造函数时,this
的绑定规则。在这种情况下,this
的值是新创建的对象。
|
|
优先级
在 JS 中,this
的绑定规则有多种,那么当多种规则同时出现时,this
的值会是什么呢?
很显然,默认绑定在优先级上是最低的,因此当多种规则同时出现时,this
的值会是除了默认绑定以外的其他绑定规则的值。
让我们比较一下隐式绑定和显式绑定的优先级。
|
|
在这里,我们可以看到,当 obj2.obj1.foo()
被调用时,this
的值是 obj1
,但是当 obj2.obj1.foo.call(obj2)
被调用时,this
的值是 obj2
。这是因为显式绑定的优先级高于隐式绑定。
再让我们比较一下隐式绑定和 new
绑定的优先级。
|
|
在这里,我们可以看到,当 obj1.foo.call(obj2, 3)
被调用时,this
的值是 obj2
,但是当 new obj1.foo(4)
被调用时,this
的值是新创建的对象 bar
。这是因为 new
绑定的优先级高于隐式绑定。
最后再让我们比较一下显式绑定和 new
绑定的优先级。
|
|
可以看出,显示绑定的优先级是要高于 new
绑定的。
bar
是被硬绑定到 obj1
上的,当你尝试使用 new bar(3)
时并没有将 obj1.a
改成 3
,反而可以被 new
覆盖,因为当我们执行 new
的时候新建了一个对象,最后我们也可以看到 baz.a
确实等于 3
。
箭头函数
在箭头函数中,this
的绑定规则与普通函数不同,它的 this
值是在定义时绑定的,而不是在执行时绑定的。这意味着,当你在箭头函数中使用 this
时,它会指向箭头函数定义时所在的上下文对象。
当箭头函数在全局被定义时,this
的值会被绑定到全局对象上。
|
|
当箭头函数在普通函数中被定义时,this
的值会指向函数定义所在的上下文对象。
|
|