您的位置:首页 > Web前端 > JavaScript

js this指向问题

2015-07-29 16:37 627 查看
js this指向问题

今天就专门总结一下js中this的指向问题。今天通过题目的方式理解一下this指向,就不从理论上深入了,理论放在以后对闭包、作用域链等总结时候再与此联系起来。

先来几条纲领:

1.函数在被直接调用的时候,其中的this指针永远指向window

2.匿名函数this总是指向window对象

3.谁执行函数,this就指向谁

4.如果函数new了一下,那么就会创建一个对象,并且this指向新创建的对象

下面通过大量例子的对比来理解this指向

[javascript] view
plaincopy

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"

-----------------------------------分割线-----------------------------------

[javascript] view
plaincopy

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

-----------------------------------分割线-----------------------------------

[javascript] view
plaincopy

function test() {

this.number = 10;

alert(this.number);

(function() {

alert(this.number);

}());

};

test();// 10 10

test方法在window环境下执行,此处第一个this指向window就没有疑问了。至于第二个,根据规则2可解释,this指向window

[javascript] view
plaincopy

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

这里有个类似的小题目,可以练习一下:

[javascript] view
plaincopy

var a = 5;

function test(){

a = 0;

console.log(a);

console.log(this.a);

var a;

console.log(a);

}

test();// ?

new test();// ?

tip: 这里还涉及到对变量提升的理解

-----------------------------------分割线-----------------------------------

[javascript] view
plaincopy

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个

-----------------------------------分割线-----------------------------------

来一道比较坑的题目^_^

[javascript] view
plaincopy

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;这里涉及到变量提升,等价代码如下:

[javascript] view
plaincopy

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,也就是:

[javascript] view
plaincopy

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下执行:

[javascript] view
plaincopy

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

-----------------------------------分割线-----------------------------------

最后还有个练习题目:

[javascript] view
plaincopy

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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: