我相信很多前端開發人員都跟我一樣,提起this
, 都能說出很多愛恨情仇...
誤區
首先我們說說this
的很多錯誤理解版本:
函數本身
這是初學者最容易走進的誤區,如果我說this
不是指函數本身,他們會覺得自己那么多年英文白學了,學習this
,首先要走出這個誤區。
注意: 很多時候我們確實需要在一個方法中獲得這個方法本身的引用, 比如遞歸調用。有以下兩種情況可供參考:
- 實名函數
public static int sum(int num){
if(1 == num){
return 1;
} else {
return num + sum(num - 1);
}
};
sum()
可以直接通過它的名字sum
在方法中獲得方法本身的引用。
- 匿名函數
setTimeout(function(){
if(some condition)
setTimeout(arguments.callee, 0); //arguments.callee is deprecated and may be removed.
}, 0);
函數的主人
this
既然不是函數自己,那是什么呢?會不會是這個方法的所屬對象?答案顯然是no。
this
究竟指什么?
在解釋這個問題之前,我們先了解一個概念:
call-site
In programming, a call site of a function or subroutine is the location (line of code) where the function is called (or may be called, through dynamic dispatch). A call site is where zero or more arguments are passed to the function, and zero or more return values are received. (from wikipedia)
根據維基百科定義,call-site指函數被調用的位置,并且在這個位置,函數獲得了它的context。看懂了吧,this
仍然可以從字面上理解為這里,但不是方法里,而是方法被調用的位置這里,因為它是在方法調用時被定義的,而不是方法聲明時被定義的。
博主:說到這里大家應該知道this
指什么了吧,那本文可以結束了...
讀者:什么,這就完了,太抽象了,可以講點具體的嗎???
哈哈,想知道this
指什么,只需要分清楚以下幾種情況:
獨立函數調用
this
指全局的Window對象或者undefined
var simpleFunction= function () {
console.log(this);//嚴格模式下 undefined, 非嚴格模式下[object Window]
};
simpleFunction();//call-site
特定上下文調用
this
指方法所屬的對象
var myObject = {}
myObject .simpleFunction= function () {
console.log(this);// myObject
};
myObject .simpleFunction();
請區別以下代碼:
var myObject = {}
myObject .simpleFunction= function () {
console.log(this);// myObject
};
var simpleFunction2 = myObject .simpleFunction;
//獲得了simpleFunction的引用,此時simpleFunction2與myObject沒有任何關系
simpleFunction2();//[object Window]
綁定上下文調用
通過apply()
call()
bind()
傳入期望的this
。
var myObject = {}
myObject .simpleFunction= function () {
console.log(this);//[object Window]
};
myObject .simpleFunction.call(this);
很多時候,以上3種方法可以幫我解決this
混亂的情況。在下面的代碼中,介紹一種叫硬綁定模式:
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a: 2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
new 綁定
設想以下代碼即將運行:
new simpleFunction(arguments)
緊接著,會發生以下事情:
- 一個繼承自simpleFunction.prototype的全新對象被創建
- simpleFunction被傳入給定參數,然后調用,
this
被綁定到新創建的對象上(new
是一種方法的調用形式,而不是類聲明)。 - 方法的返回值變成
new
表達式的值,如果沒有返回值,就用第1步創建的對象代替
由上可以看出,new
只是一種調用方式,這種調用方式可以指定方法運行時的this
。
引用MDN提供的例子:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car('Eagle', 'Talon TSi', 1993);
以上代碼創建了一個mycar對象,對象有make
model
year
3個屬性,值分別為'Eagle'
'Talon TSi'
1993
。
其他情況
setTimeout
setTimeout
是自行調用,而且是在全局環境下調用,因此往往會丟失掉期望的this
var obj = {};
obj.myMethod = function () {
console.log(this); // obj
setTimeout(function () {
console.log(this); // [object Window]
}, 100);
};
可以使用bind
setTimeout(function () {
console.log(this);
}.bind(this, 100));
也可以跳出域的限制
var that = this;
console.log(this); // this = obj
setTimeout(function () {
console.log(that); // that (this) = obj
}, 100);
某些特殊情況下this
已被綁定到好特定上下文。
forEach
function foo(el) {
console.log( el, this.id );
}
var obj = {
id: "awesome"
};
[1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
Javascript內建函數幫我們實現了foo.call(obj, index)
。
addEventListener
var element = document.querySelector('.elem');
var someMethod = function () {
console.log(this)//<div class="elem"></div>;
};
element.addEventListener('click', someMethod, false);
什么情況下我們會使用this
?
未完待續
參考: