You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
当我们学习 Javascript 中的 this 时,非常容易陷入一种困境,一种似懂非懂的困境。在某些情况下,我们看了一些文章和解释,将其应用到一些简单的情况,发现,嗯,确实这么运作了。而在另一些更为复杂的情况下,我们发现又懵逼了,什么情况?这篇文章的目的,就是要完全搞懂并掌握 Javascript 中的 this。为什么我们很难完全掌握 this?在我看来,原因是 this 的解释太过抽象,在理论上是这样,到了实际应用时,却无法直接应用。同时,一些写 this 的文章可能并未覆盖全面所有 this 的情况,如果缺乏理论和实际的互相印证,以及一些深入揭示 this 原理的实例分析,那么 this 确实是很难完全掌握的。还好,有这篇文章,看完这篇文章后,你将完全掌握 Javascript 中的 this。
确定 this 的规则
this 是 Javascript 当前执行上下文中 ThisBinding 的值,所以,可以理解为,它是一个变量。现在的关键问题是,ThisBinding 是什么?ThisBinding 是 Javascript 解释器在求值(evaluate)js 代码时维护的一个变量,它是一个指向一个对象的引用,有点像一个特殊的 CPU 寄存器。解释器在建立一个执行上下文的时候会更新 ThisBinding。
当一个函数被执行时,它的 this 值被赋值。一个函数的 this 的值是由它的调用位置决定的。但是找到调用位置,需要跟踪函数的调用链,这有时候是非常复杂和困难的。有一个更简单的确定 this 的方式,就是直接找到调用该函数的对象。Arnav Aggarwal 提出了一个简单的能确定 this 值的规则,优先级按文中的先后顺序:
1. 构造函数(constructor)中的 this,通过 new 操作符来调用一个函数时,这个函数就变成为构造函数。new 操作符创建了一个新的对象,并将其通过 this 传给构造函数。
// `global` (not `window`) refers to global object:console.log(Math===global.Math);// true// `this` doesn’t refer to the global object:console.log(this!==global);// true// `this` refers to a module’s exports:console.log(this===module.exports);// true
'use strict';functiongetThis(){console.log(this);// undefined}functionDog(saying){this.saying=saying;getThis();console.log(this);// Dog {saying: "wang wang"}}newDog('wang wang');
另一个例子,通过 this 调用构造函数中的方法,this 是 new 操作符创建的一个新对象,getThis 是该对象中的一个方法,this.getThis,符合规则 3,对象中调用对象的方法,所以 getThis 中的 this 为其调用其的对象,即 Dog {saying: "wang wang"}:
functionDog(saying){this.saying=saying;this.getThis=function(){console.log(this);// Dog {saying: "wang wang"}};this.getThis();console.log(this);// Dog {saying: "wang wang"}}newDog('wang wang');
dom 事件回调函数,包含 html 中的内联回调函数和 js 中的事件回调函数。当内联回调函数直接在定义在内联代码中或者在内联代码中被调用,它们的 this 还是 window。而如果在内联代码中直接使用 this,则 this 指向对应的 dom 元素。在 js 中监听事件,回调函数中的 this 指向对应的 dom 元素。
<h3>Using `this` "directly" inside event handler or event property</h3><buttonid="button1">click() "assigned" using addEventListner()</button><br/><buttonid="button2">click() "assigned" using click()</button><br/><buttonid="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">
used `this` directly in click event property
</button><h3>Using `this` "indirectly" inside event handler or event property</h3><buttononclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">
`this` used indirectly, inside function <br/>
defined & called inside event property</button><br/><buttonid="button4" onclick="clickedMe()">
`this` used indirectly, inside function <br/>
called inside event property
</button><br/><script>functionclickedMe(){alert(this+' : '+this.tagName+' : '+this.id);}document.getElementById('button1').addEventListener('click',clickedMe,false);document.getElementById('button2').onclick=clickedMe;</script>
eval 中的 this
eval 可以被直接或间接调用,当 eval 被间接调用时,其 this 为 global 对象。当 eval 被直接调用时,this 和它被包围处的 this 一致:
functionPoint(x,y){this.x=x;this.y=y;}varp=Point(7,5);// we forgot new!console.log(p===undefined);// true// Global variables have been created:console.log(x);// 7console.log(y);// 5// strict modefunctionPoint(x,y){'use strict';this.x=x;this.y=y;}varp=Point(7,5);// TypeError: Cannot set property 'x' of undefined
陷阱:回调函数中的 this,宽松模式下指向 window 对象
functioncallIt(func){func();}varcounter={count: 0,// Sloppy-mode methodinc: function(){this.count++;}}callIt(counter.inc);// Didn’t work:console.log(counter.count);// 0// Instead, a global variable has been created// (NaN is result of applying ++ to undefined):console.log(count);// NaN
// Inside a class or an object literal:performCleanup(){cleanupAsync().then(function(){this.logStatus('Done');// 这里会失败});}// 修复// Inside a class or an object literal:performCleanup(){cleanupAsync().then(()=>{this.logStatus('Done');});}
Javascript 中的 this
当我们学习 Javascript 中的 this 时,非常容易陷入一种困境,一种似懂非懂的困境。在某些情况下,我们看了一些文章和解释,将其应用到一些简单的情况,发现,嗯,确实这么运作了。而在另一些更为复杂的情况下,我们发现又懵逼了,什么情况?这篇文章的目的,就是要完全搞懂并掌握 Javascript 中的 this。为什么我们很难完全掌握 this?在我看来,原因是 this 的解释太过抽象,在理论上是这样,到了实际应用时,却无法直接应用。同时,一些写 this 的文章可能并未覆盖全面所有 this 的情况,如果缺乏理论和实际的互相印证,以及一些深入揭示 this 原理的实例分析,那么 this 确实是很难完全掌握的。还好,有这篇文章,看完这篇文章后,你将完全掌握 Javascript 中的 this。
确定 this 的规则
this 是 Javascript 当前执行上下文中 ThisBinding 的值,所以,可以理解为,它是一个变量。现在的关键问题是,ThisBinding 是什么?ThisBinding 是 Javascript 解释器在求值(evaluate)js 代码时维护的一个变量,它是一个指向一个对象的引用,有点像一个特殊的 CPU 寄存器。解释器在建立一个执行上下文的时候会更新 ThisBinding。
当一个函数被执行时,它的 this 值被赋值。一个函数的 this 的值是由它的调用位置决定的。但是找到调用位置,需要跟踪函数的调用链,这有时候是非常复杂和困难的。有一个更简单的确定 this 的方式,就是直接找到调用该函数的对象。Arnav Aggarwal 提出了一个简单的能确定 this 值的规则,优先级按文中的先后顺序:
1. 构造函数(constructor)中的 this,通过 new 操作符来调用一个函数时,这个函数就变成为构造函数。new 操作符创建了一个新的对象,并将其通过 this 传给构造函数。
new 操作符在 Javascript 中的实现大致如下:
2. 如果 apply,call 或者 bind 用于调用、创建一个函数,函数中的 this 是作为参数传入这些方法的参数
3. 当函数作为对象里的方法被调用时,函数内的this是调用该函数的对象。比如当obj.method()被调用时,函数内的 this 将绑定到obj对象
4. 如果调用函数不符合上述规则,那么this的值指向全局对象(global object)。浏览器环境下this的值指向window对象,但是在严格模式下('use strict'),this的值为undefined
需要注意的是,以下情况下,默认为 strict mode:
第二个需要注意的点是,在 nodejs 的 module 中,this 指向 module.exports:
5. 如果符合上述多个规则,则较高的规则(1 号最高,4 号最低)将决定this的值
6. 如果该函数是 ES2015 中的箭头函数,将忽略上面的所有规则,this被设置为它被创建时的上下文
一些进阶情况下的 this
以上确定 this 的规则,能满足大部分简单情况下 this 的确定,然而,在一些进阶情况下,我们还是难以确定 this,下面将分析一些进阶情况下的 this:
闭包中的函数中的 this
看下面这个例子,用上一节的确定 this 规则,可以看到 inner 函数中的 this 符合第四条规则,在 strict 模式下,this 为 undefined。这是因为 inner 函数有自己的 this,可以这么理解:每个非箭头函数其实都有其自己的隐式的 this 参数,而这里 inner 函数并没有明确的调用其的对象,也没有被 apply、call 或 bind,其隐式的 this 在 strict 模式下则为 undefined,在宽松模式下则为 window 对象。
一个类似的例子,在构造函数中调用外部函数:
另一个例子,通过 this 调用构造函数中的方法,this 是 new 操作符创建的一个新对象,getThis 是该对象中的一个方法,this.getThis,符合规则 3,对象中调用对象的方法,所以 getThis 中的 this 为其调用其的对象,即 Dog {saying: "wang wang"}:
回调函数中的 this
回调函数和闭包的情况类似,看下面例子:
用 new 来调用回调函数的情况,满足规则1,this 为新创建的对象:
原生 js 提供的 api 中的回调函数中的 this
setTimeout 中回调函数中的 this,在浏览器中是 window 对象,在 node 环境下为 Timeout 对象
dom 事件回调函数,包含 html 中的内联回调函数和 js 中的事件回调函数。当内联回调函数直接在定义在内联代码中或者在内联代码中被调用,它们的 this 还是 window。而如果在内联代码中直接使用 this,则 this 指向对应的 dom 元素。在 js 中监听事件,回调函数中的 this 指向对应的 dom 元素。
eval 中的 this
eval 可以被直接或间接调用,当 eval 被间接调用时,其 this 为 global 对象。当 eval 被直接调用时,this 和它被包围处的 this 一致:
this 实例
修复方式:
修复方式有很多,个人比较喜欢的方式是直接用箭头函数:
this 最佳实践
参考文献
The text was updated successfully, but these errors were encountered: