高程上的大前提:
1、this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于window,而当函数被作为某个对象的方法调用时,this 等于那个对象;不过,匿名函数的执行环境具有全局性,因此其this 对象通常指向window。
2、每个函数在被调用时都会自动取得两个特殊变量:this 和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量;
总结:
1、在事件处理程序中,事件绑定在哪个元素身上,this 就指向谁;分析戳这里;
2、在方法调用(如果某个对象的属性是函数,这个属性就叫方法,调用这个属性,就叫方法调用)中,执行函数体的时候,作为属性访问主体的对象和数组便是其调用方法内 this 的指向。(通俗的说,调用谁的方法 this 就指向谁;)
3、普通函数执行里的 this 都是 全局对象,非严格模式下浏览器端指的是 window(严格模式下 undefined,因为全局变量对象里没有定义this),因为都是在全局环境下执行的;
4、任意函数里如果嵌套了 函数,那这个时候 嵌套函数里的 this 在未指定的情况下,应该指向的是 window 对象(浏览器中非严格模式下,严格模式下返回 undefined ,因为嵌套函数里活动对象未指定 this);如果想在嵌套函数中继续用原来的 this 指向,需要保存下,比如 var self = this ,然后在嵌套函数中使用 self 变量,因为 this 不会传递;
这里加个案例:
var arr = [1, 2, 3]; arr.map(function() { console.log(this); });
你觉得这里 this 是 arr 对象么?
不是的,全局环境下,输出三个 window,你可能以为上面的理论错了,其实不是,map 作为 arr 对象的方法,在 map 函数里访问 this 当然是 arr 对象,而这里相当于在 arr.map 函数里又嵌套了一层函数,没指定的情况下,this 当然是 window,这里容易混淆,注意理解;
这里重写了默认的 map 方法,方便理解:
var arr = [1, 2, 3]; arr.map = function(fn) { console.log(this); //输出 arr 对象 fn(); }; arr.map(function() { console.log(this);//输出 window ,全局环境下 });
如果要把 map 里的 this 保存到嵌套的函数里:
var arr = [1, 2, 3]; arr.map = function(fn) { fn.call(this); // 法一:把 this 作为参数传进去,替换了原来 fn 中的 this var self=this; // 法二:保存下来,在嵌套函数里用 (function () { console.log(self); }()); }; arr.map(function() { console.log(this); });
这样就输出两次 arr 对象了,call 不会用点这里
5、通过构造函数创建对象,在调用构造函数之前就已经创建了新对象,并将构造函数的作用域赋给了新对象,因此构造函数里的 this 指向的就是这个新对象;
6、with 语句并没有改变 this 的指向,不过表现确实有点奇怪,看截图:
7、一些特殊情况,如:逗号运算符,赋值运算符
1 var name = "The Window"; 2 var object = { 3 name: "My Object", 4 getName: function() { 5 return this.name; 6 } 7 }; 8 object.getName(); //"My Object" 9 (object.getName)(); //"My Object" 10 (object.getName = object.getName)(); //"The Window",在非严格模式下 11 (object.getName, object.getName)(); //"The Window",在非严格模式下
第9行代码解释:
虽然加上括号之后,就好像只是在引用一个函数,但this 的值得到了维持,因为object.getName 和(object.getName)的定义是相同的。
根据 MDN ,圆括号运算符()
用来控制表达式中的运算优先级。所以书上说,没有改变 this,我觉得是说的过去的;
第10行代码解释:
代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以this 的值不能得到维持,结果就返回了"The Window"。
我的理解是,赋值表达式返回了内存中的一个值,也就是函数本身,而不是原来方法的引用,所以 this 没有得到维持,结果返回了 window;
第11行代码解释:
同上,因为根据 MDN ,逗号操作符 也是返回最后一个操作数的值,也是函数本身,所以和上面一样;
按照这个逻辑的话,所有返回值操作的,都有可能去改变上面代码中的 this 值,都需要谨慎,譬如:
var a = { func: function() { return this; } }; function f(fn) { console.log(fn()); } f(a.func);//window
因为函数中的参数也是按照值传递的,在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量。
参考资料: