簡介
在絕大數情況下,函數的調用方式決定了this
值。this
不能在執行期間被復制,並且在每次函數被調用時this
的值也可能會不同。
this
的值表示當前執行的環境對象,而與聲明環境無關,所以this
代表的對象要等函數運行。類似定義函數時的參數列表,只有在函數調用時才傳入真正的對象。
this
關鍵字雖然會根據環境變化,但它始終代表的是調用當前函數的對象。
全局環境
無論是否在嚴格模式下,在全局執行環境中(在任何函數體外部),this
都指向全局對象。
console.log(this === window); //true
函數環境中的this
在函數內部,this
的值取決於函數被調用的方式。
簡單調用
在非嚴格模式下,this
的值不是由該調用設置的,所以this
的值默認指向全局對象。
function f1() {
return this;
}
f1() === window;
在嚴格模式下,this
將保持他進入執行環境時的值,所以this
將會默認為undefined
:
function f2() {
"use strict";
return this;
}
f2() === undefined;
在嚴格模式下,如果this
沒有執行環境定義,那它將保持為undefined
。
如果想把this
的值從一個環境傳到另一個,就要用call
或者apply
方法。
//將一個對象作為call和apply的第一個參數,this就會被綁定到這個對象。
var obj = {a: 'Custom '};
var a = "Global";
function whatsThis(arg) {
return this.a;
}
whatsThis(); //'Global'
whatsThis.call(obj); //'Custom'
whatsThis.apply(obj); //'Custom'
當一個函數在其主體中使用this
關鍵字時,可以使用函數繼承自Function.prototype
的call
或apply
方法將this
值綁定到調用中的特定對象。
使用call
和apply
函數的時候,如果傳遞給this
的值不是一個對象,JavaScript會嘗試使用內部toObject
操作將其轉換為對象。
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); //[object Number]
bind方法
ES5引入了Function.prototype.bind
。調用f.bind(someObject)
會創建一個與f
具有相同函數體和作用域的函數,但是在這個新函數中,this
將永久的被綁定到了bind
的第一個參數,無論這個函數是如何被調用的。
function f() {
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); //azerty
var h = g.bind({a:"yoo"}); //bind只生效一次,所以此處bind無效
console.log(h()); //azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); //37, azerty, azerty
箭頭函數
在箭頭函數中,this
與普通函數的指向不同,它與封閉詞法環境的this
保持一致。在箭頭函數中,this
指向創建時所在的環境,這同樣適用於在其他函數內創建的箭頭函數;在普通函數中,this
指向執行時的環境。
箭頭函數被調用的時候,不會自動綁定一個this對象。箭頭對象根本沒有自己的this,它的this都是捕獲自其所在上下文的this值。
舉栗子:
var foo = (( => this));
console.log(foo() === window); //true
//作為對象的一個方法調用
var obj = {foo: foo};
console.log(obj.foo() === window); //ture
//使用call來設定this
console.log(foo.call(obj) === window); //true
//使用bind設定this
foo = foo.bind(obj);
console.log(foo() === window); //true
第二個栗子:
//bar返回一個函數,這個函數返回this
//這個返回的函數是以箭頭函數創建的
//所以它的this被綁定到了外層函數的this。
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
//作為obj對象的一個方法來調用bar,把它的this綁定到obj。將返回的函數的引用復制給fn
var fn = obj.bar();
//直接調用fn而不設置this
//通常不適用箭頭函數的情況,默認為全局對象
//若在嚴格模式則為undefined
console.log(fn() === obj); //true
//如果只是引用obj的方法,而沒有調用它
var fn2 = obj.bar; //fn2 = function() {var x = (() => this); return x;}
//調用箭頭函數后,this指向window,因為它從bar繼承了this
console.log(fn2() === window); /true
作為對象的方法
當函數作為對象里的方法被調用時,它們的this
是調用該函數的對象。
栗子:
var o = {
prop: 37
f: function() {
return this.prop;
}
}
console.log(o.f()); //logs 37
函數時從o
的f
成員調用才是重點。
同樣,this
的綁定只受最靠近的成員應用的影響。
o.b = {g: independent, prop: 42}
console.log(o.b.g()) //42
這個栗子說明,這與它是對象o
的成員沒有什么關系,最靠近的引用才是最重要的。
原型鏈中的this
對於在對象原型鏈上某處定義的方法,同樣的概念也使用。如果該方法存在於一個對象的原型鏈上,那么this
指向的是調用這個方法的對象,就像是該方法在對象上一樣。
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); //5
在這個栗子中,對象p
沒有屬於自己的屬性,它的f
屬性繼承自它的原型。雖然在對於f
的查找過程中,最終是在o
中找到的f
屬性,但是查找股從橫首先從p.f
的引用開始,所以函數中的this
指向p
。也就是說f
是作為p
的方法調用的,所以它的this
指向了p
。這是JavaScript的原型繼承中的一個有趣的特征。
getter與setter中的this
同樣的概念適用於,當函數在一個getter
或者setter
中被調用。用作getter
或setter
的函數都會把this
綁定到設置或獲取屬性的對象。
function sum() {
return this.a + this.b + this.c;
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum, enumerable: true, configurable: trye
})
console.log(o.average, o.sum); //logs 2, 6
作為構造函數
當一個函數用作構造函數時(使用new關鍵字),它的this
被綁定到正在構造的新對象。
雖然構造器返回的默認值是this
所指的對象,但是也可以手動地返回其他的對象(如果返回值不是一個對象,則返回this
對象)。
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); //logs 37
function C2() {
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); //logs 38
在這個栗子中,在調用構造函數的過程中,手動的設置了返回對象,所以默認對象就被丟棄了。this.a = 37;
這條語句雖然執行了,但是對於外部沒有任何影響。
作為一個DOM事件處理函數
當函數被用作事件處理函數時,它的this
指向觸發事件的元素。
作為一個內聯事件處理函數
當代碼被內聯on-event處理函數調用時,它的this
指向監聽器所在的DOM元素:
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
這個栗子的alert會顯示button
。只有外層代碼中的this
是這樣設置的:
<button onclick="alert((function() {return this})());">
Show inner this
</button>
在這種情況下,沒有設置內部函數的this
,所以它指向window/global對象,非嚴格模式下,調用的函數未設置this
時指向的默認對象。