Javascript 中 this 的指向
关于 javascript 中 this 用法的笔记,来自 You Don’t Know JS: this & Object Prototypes 的读书笔记。内容包括 this 绑定规则的总结,以及遇到具体问题时,判断 this 指向的方法。
首先了解下 this 的绑定规则,分别是 默认绑定
、显式绑定
、隐式绑定
、new 绑定
,说是绑定规则,我的理解是四种应用场景,不同的应用场景对应着不同的指向。
指向规则
默认绑定
默认绑定的方式很常见,举个例子
1 | function fun() { |
在默认绑定规则下,this 被绑定到了全局对象,所以例子中输出的 this.a
的值即全局变量 a
的值。
需要注意的是,绑定到全局对象只存在于非严格模式下,当使用严格模式时,this 会被绑定到 undefined。参见下面的案例。
1 | ; |
隐式绑定
相比于默认绑定只有两个选项,隐式绑定就含蓄的多,我们需要分析上下文来弄清 this 的指向。
1 | function fun() { |
foo 作为引用属性被调用,虽然 foo 不属于 obj 对象,但在被调用时,foo 落脚于 obj 对象,隐式绑定将 this 绑定到此时的上下文对象,所以 此时的 this.a
和 obj.a
是一样的。
这一个例子还不能完全将隐式绑定解释清晰,为此,我们需要更多的例子,下面我们来讨论下引用
1 |
|
如果遇到这样的引用链,稍加分析不难理解,对象属性引用链中只有最顶层或者说最后一层会影响 this 的上下文,换而言之,在分析引用链中的 this 指向,只需要分析最后一层即可。
1 | function fun() { |
在这段代码中,bar
引用 obj.foo
,obj.foo
引用 fun
,归根结底,bar
引用的还是 fun
,最后在调用时,和 obj
并没有关系,这只是开始,下面继续。
1 | function fun() { |
这段代码中的参数传递其实就是一种隐式传值,看到这里有没有觉得似曾相识,跟上一段代码异曲同工,表面上调用的是 obj.foo
其实与 obj
并无联系。doFoo()
中执行的 foo()
调用的是 fun()
。趁热打铁,再来一个例子。
1 | function fun() { |
这段代码和上一段并无二致,只不过这种情形在开发中经常会遇到,this 丢失问题,有没有好办法,肯定有,往下看。
显式绑定
显式绑定通过 call(…)
、apply(…)
方法强制绑定 this
1 | function fun() { |
call(…)
方法将本来指向 obj1
上下文对象的 this 绑定到了 obj2
,光明正大的挖墙脚……
为了解决之前的 this 丢失问题,在显式绑定的基础上升级出了硬绑定,此招一出,墙脚硬到磕坏牙,再也不用担心 this 会丢失了。
1 | function fun() { |
看到没,稳如泰山。硬绑定在 ES5 之后有了现成可用的方法 Function.prototype.bind
,其中一种应用场景如下:
1 | function fun(something) { |
new 绑定
考虑下面的代码
1 | function fun(a) { |
使用 new 来调用 fun(..)
时,会构造一个新对象并把它绑定到 fun(..)
调用中的 this
上。new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
New 绑定也是四种绑定中优先级最高的一种,连硬绑定也能掰弯
1 | function fun(a) { |
在 new 对象时,预期 obj.a
值应该赋为 3,结果并没有修改,而是 new 出来一个新的对象,说明在 new 的过程中,this 的指向改变了。
判断方法
常规
四种规则优先级先后顺序为: new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
。
根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照下面的顺序来进行判断:
函数是否在new中调用 ? 如果是的话this绑定的是新创建的对象。
var bar = new fun();
函数是否通过call、apply或者硬绑定调用 ? 如果是的话,this绑定的是 指定的对象。
var bar = fun.call(obj);
函数是否在某个上下文对象中调用 ? 如果是的话,this 绑定的是那个上 下文对象。
var bar = obj.foo();
如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到 全局对象。
var bar = fun();
作者: leeon
来源: https://leeon.im
链接: https://leeon.im/this-in-javascript/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可