js this指向问题

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 10
test方法在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);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值