js this指向问题
今天就专门总结一下js中this的指向问题。今天通过题目的方式理解一下this指向,就不从理论上深入了,理论放在以后对闭包、作用域链等总结时候再与此联系起来。
先来几条纲领:
1.函数在被直接调用的时候,其中的this指针永远指向window
2.匿名函数this总是指向window对象
3.谁执行函数,this就指向谁
4.如果函数new了一下,那么就会创建一个对象,并且this指向新创建的对象
下面通过大量例子的对比来理解this指向
var name = "win"; var obj = { name: "obj", func: function() { var self = this; console.log(this.name);// obj console.log(self.name);// obj //a方法 (function() { console.log(this.name);// win console.log(self.name);// obj }()); //b方法 (function() { console.log(this.name);// win console.log(self.name);// obj })(); setTimeout(function(){ console.log(this.name);// win console.log(self.name);// obj }, 100); } } obj.func();这个题目中,用了一个self的变量将this的指向保存了下来,obj外面的name是个全局变量。
a方法和b方法效果其实是一样的,因为它们都是匿名函数,根据规则2,此时this指向window,因此this.name相当于window.name,结果自然是"win"
setTimeout中第一个参数同样是一个匿名函数,它的this同样指向window,结果同样是"win"
而self是obj事先保存下来的this指向,它始终指向obj,因此this.name相当于obj.name,结果自然是"obj"
-----------------------------------分割线-----------------------------------
var name = "the window"; var obj = { name: "my object", getName: function() { return this.name; } }; //1 console.log( obj.getName() ); // my object //2 console.log( (obj.getName = obj.getName)() ); // the window //3 var temp = obj.getName; temp(); // the window //4 console.log( obj.getName.call(window) ); // the window //5 console.log( obj.getName.apply(window) ); // the window //6 console.log( obj.getName.bind(window)() ); // the window第1个应该没什么疑问,this指向obj
第2个这种写法将this指向改变了,this指向window
第3个与第2个类似,temp中window环境下执行,因此this指向window
第4、5、6都是强行将方法getName绑定到window上执行,因此this很自然的指向window
-----------------------------------分割线-----------------------------------
function test() { this.number = 10; alert(this.number); (function() { alert(this.number); }()); }; test();// 10 10test方法在window环境下执行,此处第一个this指向window就没有疑问了。至于第二个,根据规则2可解释,this指向window
function test() { this.number = 10; alert(this.number); (function() { alert(this.number); }()); }; new test();// 10 undefined此处函数test被new了一下,根据规则4,如果函数new了一下,那么就会创建一个对象,并且this指向新创建的对象,此时第一个this指向test对象。第二个根据规则2,this指向window
这里有个类似的小题目,可以练习一下:
var a = 5; function test(){ a = 0; console.log(a); console.log(this.a); var a; console.log(a); } test();// ? new test();// ?tip: 这里还涉及到对变量提升的理解
-----------------------------------分割线-----------------------------------
var length = 100; function fn(){ console.log(this.length) } function Test(a, b){ var t1 = arguments.length; var t2 = Test.length; console.log(arguments[0]); console.log(a); console.log(a === arguments[0]);// true a();// 100 arguments[0]();// 4 console.log(t1, t2);// 4 2 } Test(fn, fn, fn, fn);arguments.length表示实际参数个数,Test.length表示定义形参个数
大家可以思考下a()和结果与arguments[0]()执行的结果为什么不相同呢?明明 a === arguments[0] 返回true
原来,就是因为this在作怪。a()执行时是在window环境下的,因此this指向window。而arguments[0]()执行时却是在对象arguments环境下执行的,this指向arguments,求它的length属性,自然是4,因为它的实际参数确实是4个
-----------------------------------分割线-----------------------------------
来一道比较坑的题目^_^
var number = 2; var obj = { number: 4, fn1: (function(){ this.number *= 2; number = number * 2; var number = 3; return function(){ this.number *= 2; number *= 3; console.log(number); } })(), fn2: function(){ this.number *= 2; } }; //第一步 var fn1 = obj.fn1; //结果: window.number == 4, number == 3, obj.number == 4 console.log(number);// 4 //第二步 fn1();// 9 //结果: window.number == 8, number == 9, obj.number == 4 //第三步 obj.fn1();// 27 //结果: window.number == 8, number == 27, obj.number == 8 console.log(window.number);// 8 console.log(obj.number);// 8
首先要明确:obj.fn1在定义的时候就执行了前面3行,并返回了一个函数,由于是闭包,obj.fn1中的内容执行完毕后并没有被销毁,而是保留在了内存中。
第一步,将obj.fn1执行后返回的函数赋给全局变量fn1。
函数执行时,this.number *= 2,根据规则2,this指向window,相当于执行window.number *= 2,原本window.number定义为2,因此此时window.number == 4。
number = number * 2; var number = 3;这里涉及到变量提升,等价代码如下:
var number; number = number * 2; number = 3;第1行执行完毕number为undefined;第2行执行完number为NaN;第3行执行完number为3;因此这里的number = number * 2;并没有什么卵用
好了,第一步执行完,整理一下此时各个变量的值:
window.number == 4, number == 3, obj.number == 4
第二步,执行函数fn1,也就是:
this.number *= 2; number *= 3; console.log(number);此时在window下执行fn1,因此this指向window,上一步执行完window.number == 4,执行完this.number *= 2后window.number变成了8
轮到执行number *= 3了,我们发现fn1中并没有number的定义,需要到上一级的作用域中寻找number,之前已经说了,闭包并没有销毁,依然保存在了内存中,第一步执行完后结果为3,因此这里执行完number *= 3后number的值变为9
第二步执行完,整理一下此时各个变量的值:
window.number == 8, number == 9, obj.number == 4
第三步,执行obj.fn1,根据规则3,此时this指向obj,obj.number == 4
obj下执行:
this.number *= 2; number *= 3; console.log(number);第1行,this.number *= 2相当于obj.number *= 2,执行完后,obj.number == 8;
第2行,number *= 3执行,原来保存下来的number == 9,执行完后,number == 27
第三步执行完,整理一下此时各个变量的值:
window.number == 8, number == 27, obj.number == 8
-----------------------------------分割线-----------------------------------
最后还有个练习题目:
function test() { this.data = 5; this.log = function() { console.log(this.data); } } var a = new test(); a.log();// ? (a.log)();// ? (a.log = a.log)();// ? var b = a.log; b();// ? (function(){ a.log();// ? })(); setTimeout(a.log, 100);// ? setTimeout(function(){ a.log();// ? }, 100);
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/u011700203/article/details/47123889