1. 程式人生 > >this以及apply,call,bind之間的區別

this以及apply,call,bind之間的區別

關於this物件this物件是在執行時基於函式的執行環境繫結的:在全域性環境中,this等於window,而當函式被作為某個物件的方法呼叫時,this等於那個物件。不過,匿名函式的執行環境具有全域性性,因此其this物件通常指向window。------《Javascript高階程式設計》

每個函式都有自己的執行環境。全域性執行環境是最外圍的一個執行環境。this指向的就是當前程式碼所在的執行環境。

除了文字的解釋,再來幾個直白的例子:
// 定義一個全域性變數
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.type,所以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高階程式設計》

以上。