在网上不少文章都对 Javascript 中的 this 作了详细的介绍,但大可能是介绍各个绑定方式或调用方式下 this 的指向,因而我想有一个统一的思路来更好理解 this 指向,使你们更好判断,如下有部份内容不是原理,而是一种解题思路。segmentfault
call 方法容许切换函数执行的上下文环境(context),即 this 绑定的对象。浏览器
大多数介绍 this 的文章中都会把 call 方法放到最后介绍,但此文咱们要把 call 方法放在第一位介绍,并从 call 方法切入来研究 this ,由于 call 函数是显式绑定 this 的指向,咱们来看看它如何模拟实现(不考虑传入 null 、 undefined 和原始值):闭包
Function.prototype.call = function(thisArg) { var context = thisArg; var arr = []; var result; context.fn = this; for (let i = 1, len = arguments.length; i < len; i++) { arr.push('arguments[' + i + ']'); } result = eval("context.fn(" + arr + ")"); delete context.fn; return result; }
从以上代码咱们能够看到,把调用 call 方法的函数做为第一个参数对象的方法,此时至关于把第一个参数对象做为函数执行的上下文环境,而 this 是指向函数执行的上下文环境的,所以 this 就指向了第一个参数对象,实现了 call 方法切换函数执行上下文环境的功能。dom
在模拟 call 方法的时候,咱们使用了对象方法来改变 this 的指向。调用对象中的方法时,会把对象做为方法的上下文环境来调用。函数
既然 this 是指向执行函数的上下文环境的,那咱们先来研究一下调用函数时的执行上下文状况。this
下面我门来看看调用对象方法时执行上下文是如何的:url
var foo = { x : 1, getX: function(){ console.log(this.x); } } foo.getX();
从上图中,咱们能够看出getX
方法的调用者的上下文是foo
,所以getX
方法中的 this 指向调用者上下文foo
,转换成 call 方法为foo.getX.call(foo)
。spa
下面咱们把其余函数的调用方式都按调用对象方法的思路来转换。prototype
function Foo(){ this.x = 1; this.getX = function(){ console.log(this.x); } } var foo = new Foo(); foo.getX();
执行 new 若是不考虑原型链,只考虑上下文的切换,就至关于先建立一个空的对象,而后把这个空的对象做为构造函数的上下文,再去执行构造函数,最后返回这个对象。code
var newMethod = function(func){ var context = {}; func.call(context); return context; } function Foo(){ this.x = 1; this.getX = function(){ console.log(this.x); } } var foo = newMethod(Foo); foo.getX();
DOMElement.addEventListener('click', function(){ console.log(this); });
把函数绑定到DOM事件时,能够看成在DOM上增长一个函数方法,当触发这个事件时调用DOM上对应的事件方法。
DOMElement.clickHandle = function(){ console.log(this); } DOMElement.clickHandle();
var x = 1; function getX(){ console.log(this.x); } getX();
这种状况下,咱们建立一个虚拟上下文对象,而后普通函数做为这个虚拟上下文对象的方法调用,此时普通函数中的this就指向了这个虚拟上下文。
那这个虚拟上下文是什么呢?在非严格模式下是全局上下文,浏览器里是 window ,NodeJs里是 Global ;在严格模式下是 undefined 。
var x = 1; function getX(){ console.log(this.x); } [viturl context].getX = getX; [viturl context].getX();
var x = 1; var foo = { x: 2, y: 3, getXY: function(){ (function(){ console.log("x:" + this.x); console.log("y:" + this.y); })(); } } foo.getXY();
这段代码的上下文以下图:
这里须要注意的是,咱们再研究函数中的 this 指向时,只须要关注 this 所在的函数是如何调用的, this 所在函数外的函数调用都是浮云,是不须要关注的。所以在全部的图示中,咱们只须要关注红色框中的内容。
所以这段代码咱们关注的部分只有:
(function(){ console.log(this.x); })();
与普通函数调用同样,建立一个虚拟上下文对象,而后普通函数做为这个虚拟上下文对象的方法当即调用,匿名函数中的 this 也就指向了这个虚拟上下文。
var x = 1; var foo = { x: 2, getX: function(){ console.log(this.x); } } setTimeout(foo.getX, 1000);
函数参数是值传递的,所以上面代码等同于如下代码:
var getX = function(){ console.log(this.x); }; setTimeout(getX, 1000);
而后咱们又回到了普通函数调用的问题。
全局中的 this 指向全局的上下文
var x = 1; console.log(this.x);
var x = 1; var a = { x: 2, b: function(){ return function(){ return function foo(){ console.log(this.x); } } } }; (function(){ var x = 3; a.b()()(); })();
看到上面的状况是有不少个函数,但咱们只须要关注 this 所在函数的调用方式,首先咱们来简化一下以下:
var x = 1; (function(){ var x = 3; var foo = function(){ console.log(this.x); } foo(); });
this 所在的函数 foo 是个普通函数,咱们建立一个虚拟上下文对象,而后普通函数做为这个虚拟上下文对象的方法当即调用。所以这个 this指向了这个虚拟上下文。在非严格模式下是全局上下文,浏览器里是 window ,NodeJs里是 Global ;在严格模式下是 undefined 。
在须要判断 this 的指向时,咱们能够安装这种思路来理解:
判断 this 在全局中OR函数中,若在全局中则 this 指向全局,若在函数中则只关注这个函数并继续判断。
判断 this 所在函数是否做为对象方法调用,如果则 this 指向这个对象,不然继续操做。
建立一个虚拟上下文,并把this所在函数做为这个虚拟上下文的方法,此时 this 指向这个虚拟上下文。
在非严格模式下虚拟上下文是全局上下文,浏览器里是 window ,Node.js里是 Global ;在严格模式下是 undefined 。
图示以下:
欢迎关注:Leechikit
原文连接:segmentfault.com到此本文结束,欢迎提问和指正。写原创文章不易,若本文对你有帮助,请点赞、推荐和关注做者支持。