this以及apply,call,bind之间的区别

时间:2022-12-31 16:14:53
关于this对象
this对象是在运行时基于函数的执行环境绑定的:在全局环境中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。------《Javascript高级程序设计》

除了书里的解释,再来几个直白的例子:
// 定义一个全局变量
var type = "全局"; 

// 定义函数aa
function aa () {
	var type = '局部';
	console.log(this.type);
}
aa(); // "全局"

// 定义对象bb
var bb = {
	type: '局部',
	getType: function () {
		console.log(this.type)
	}
};
bb.getType(); // “局部”

// 定义对象cc
var cc = {
	type: '局部',
	getType: function () {
		return function () {
			return this.type
		}
	}
};
cc.getType(); // “全局”(非严格模式下)
第一个例子,aa函数运行在全局环境中,所以this指向window。
第二个例子,bb是一个对象,调用bb的getType方法,getType的运行环境在bb对象中,this指向bb。
第三个例子,同bb对象,不同的是cc的getType方法返回的是一个匿名函数,匿名函数中又返回this.type。为什么this并没有像bb中一样指向bb,这个和JS的活动对象有关。每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。所以this还是指向window。

看起来似乎挺好理解的,但在实际中在某些特殊情况下,this的值可能会意外改变。这个在实际生产中,还是需要认真去分析的。

改变this指向的几种方法以及之间的区别:
apply()和call()
每个函数都包含两个非继承而来的方法: appy()和call()。这两个方法的用途都是在特定的作用域中调用函数,然后可以设置调用函数的this指向。

首先, apply()方法接受两个参数,一是作用域,二是参数(可以是数组也可以是arguments对象)。
例如:
function test (test1, test2) {
console.log(test1 + test2);
}
function applyTest1 (test1, test2) {
return test.apply(this, [test1, test2]); // 传入数组
}

function applyTest2 (test1, test2) {
return test.apply(this, arguments); // 传入arguments
}
applyTest1 (50, 50); // 100
applyTest2 (50, 50); // 100
这个例子里将test函数的this指向了applyTest系列函数,同时参数为applyTest系列函数传入的参数为数组和arguments对象都可以正常执行并返回正确的结果。

call()的使用和 apply()基本是一样的,不同的是参数的传递, call()必须明确的传入每一个参数。
例如:
function test (test1, test2) {
console.log(test1 + test2);
}
function applyTest1 (test1, test2) {
return test.call(this, test1, test2); // 明确传入参数
}
当然,传递参数并不是 apply()和call()真正的用武之地,其真正强大的地方还是在于通过传入作用域来扩充函数赖以运行的作用域。通过传入的作用域不同,来改变函数的this指向,这可以让我们做很多事情。

bind()
ECMAScript还定义了一个叫 bind()的方法。这个方法会创建一个函数的实例,其this值会被绑定给传入 bind()函数的值。
例子:
var name = "名字";
var person = {name: "小明"};
function sayHello () {
console.log(this.name + ': hello')
}
var personSay = sayHello.bind(person);
personSay(); // 小明:hello
上面例子通过bind将sayHello的实例赋给了personSay,同时将this指向了person对象,所以此时即使是在全局作用域中调用这个函数,也会看到sayHello实例上的this.name指向的是blue;

参考文献: 《Javascript高级程序设计》


以上。