--- title: this slug: Web/JavaScript/Reference/Operators/this tags: - JavaScript - this translation_of: Web/JavaScript/Reference/Operators/this ---
JavaScript 函式內的 this
關鍵字表現,和其他語言相比略有差異。在嚴格模式與非嚴格模式下也有所不同。
通常,this
值由被呼叫的函式來決定。它不能在執行期間被指派,每次函式呼叫調用的值也可能不同。ES5 引入了 bind
方法去設置函式的 this
值,而不管它怎麼被呼叫。ECMAScript 2015 也導入了定義 this
詞法範圍的箭頭函式(它的 this
值會維持在詞法作用域)。
this
this
值在所有函式以外的全域執行環境下,會被當作全域物件,無論是否處於嚴格模式。
console.log(this.document === document); // true // 在網路瀏覽器中,window 物件也是全域物件。 console.log(this === window); // true this.a = 37; console.log(window.a); // 37 this.b = "MDN"; console.log(window.b) // "MDN" console.log(b) // "MDN"
在函式內的 this
值取決於該函式如何被呼叫。
因為以下程式碼並不處於嚴謹模式下、而 this
值也沒被呼叫(call)設定,this
會變成全域物件,在瀏覽器之下則會變成 window
。
function f1(){ return this; } //在瀏覽器中: f1() === window; // true //Node中: f1() === global; // true
然而,在嚴格模式下,this
值會在進入執行環境時建立並維持該狀態。因此,下例的 this
預設值是 undefined
:
function f2(){ "use strict"; // 嚴格模式 return this; } f2() === undefined; //true
所以在嚴格模式下,如果 this
沒有定義到執行環境內,其預設值就會是 undefined
。
備註:在第二個例子裡面,this
應為 undefined
,因為 f2
是直接被呼叫,而不是在其為某個物件的方法或屬性的情況下(例如 window.f2()
)被直接呼叫。某些瀏覽器首次支援嚴格模式時沒導入這個特徵,它們會因此錯誤的回傳 window
物件。
要從某個語境訪問另一個 this
語境的值,請使用 call 或 apply:
// 物件可以被當作call或apply的第一個參數,而this會綁定該物件 var obj = {a: 'Custom'}; // 此屬性a為全域物件 var a = 'Global'; function whatsThis(arg) { return this.a; // this 值取決於此函數如何被呼叫 } whatsThis(); // 'Global' whatsThis.call(obj); // 'Custom' whatsThis.apply(obj); // 'Custom'
當函式內部調用 this
關鍵字時,其值會和所有繼承自 Function.prototype
並使用 call
或 apply
方法呼叫的特定物件綁定。
function add(c, d) { return this.a + this.b + c + d; } var o = {a: 1, b: 3}; // 第一個參數(parameter)是調用了 this 的物件, // 後續參數(parameters)會作為函式呼叫內的參數(arguments)而通過 add.call(o, 5, 7); // 16 // 第一個參數(parameter)是調用了 this 的物件, // 第二個參數的陣列作為函式呼叫內的參數(arguments)之構件 add.apply(o, [10, 20]); // 34
使用 call
和 apply
時,如果被輸入作為 this
的值不是物件,JavaScript 內建的 ToObject
運算符會試著把被輸入的值轉變為物件。如果被輸入的值是一個原始型別,例如 7
或是 'foo'
,它們會自動被相關的建構方法轉變為物件。因此,原始數值7
會轉變成類似用new Number(7)
產生的物件,而字串'foo'
會轉變成類似用new String('foo')
產生的物件。
function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // [object Number] bar.call('foo'); // [Object String]
bind
方法ECMAScript 5 導入了 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 只能使用一次!
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
值。在全域程式碼內,則設為全域物件:
var globalObject = this; var foo = (() => this); console.log(foo() === globalObject); // true
備註:如果這參數被傳遞給箭頭函式的 call, bind, apply 調用,該參數會被忽略。你仍然可以將參數預先調用到call,但第一個參數(thisArg)必須設置為空。
// 作為物件的方法呼叫 var obj = {foo: foo}; console.log(obj.foo() === globalObject); // true // 使用呼叫以嘗試設置 this console.log(foo.call(obj) === globalObject); // true // 使用 bind 以嘗試設置 this foo = foo.bind(obj); console.log(foo() === globalObject); // true
無論以上哪種,foo
的 this
在建立的時候,都會設為原本的樣子(以上面的例子來說,就是全域物件)。這同樣適用於在其他函式內創建的箭頭函式:它們的 this
是設置為外部執行上下文。
// 建立一個物件,其方法 bar 含有回傳自己的 this 函式。回傳函式作為箭頭函數而建立, // 因此該函式的 this 將永遠與外圍函式(enclosing function)的 this 綁定。 // bar 的值可在呼叫內設立,which in turn sets the value of the returned function. var obj = { bar : function() { var x = (() => this); return x; } }; // 將 bar 作為物件的方法呼叫,把它的 this 設為物件 // 指派 fn 作為回傳函數的參照(reference) var fn = obj.bar(); // 在不設置 this 情況下呼叫的 fn,通常默認為全域物件,在嚴格模式下則是 undefined console.log(fn() === obj); // true
以上面的程式碼為例,稱作匿名函數(anonymous function)A 的函數被指定為 obj.bar
,它回傳的函數(稱作匿名函數 B)作為箭頭函數而建立。因而,函數 B 的 this
在呼叫時,將永遠設為 obj.bar
(函數 A)的 this
。呼叫被回傳的函數(函數 B)時,它的 this
將一直是原本的樣子。
再以上面的程式碼為例,函數 B 的 this
被設為函數 A 的 this
,而它屬於物件,所以它依然會設為 obj
,就算在 this
設為 undefined
或全域物件的呼叫方式下(或在全域執行環境下,上例的任何方法)
如果一個函式是以物件的方法呼叫,它的 this
會設為該呼叫函式的物件。
以下面的程式碼為例,呼叫 o.f()
的時候,函式內的 this
會和 o
物件綁定。
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
請注意這個行為,不會受函式如何或何處定義所影響。以上面為例,在我們定義 o
時,也定義了行內函式 f
作為構件(member)。不過,我們也能先定義函式,接著讓它附屬到 o.f
。這麼做會得出相同的效果:
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // 37
這表明了 this
只和 f
作為 o
的構件呼叫有所關聯。
同樣的,this
綁定只會受最直接的構件引用(most immediate member reference)所影響。在下面的例子,我們將物件 o.b
的方法 g
作為函式呼叫。在執行的期間,函式內的 this
會參照 o.b
。物件是否為 o
的構件無關緊要,最直接的引用才是最緊要的。
o.b = {g: independent, prop: 42}; console.log(o.b.g()); // logs 42
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
屬性, 它(p)繼承了它(o)的原型。但是查找 f
最終在 o
上找到它的成員名為 f 並不重要。查找開始作為 p.f
的引用,所以 this
在函式內部物件的值被當作是p
的引用。也就是說,f
作為 p
的調用方法以來, 它的 this
指的就是 p
. 這是一個非常有趣的JavaScript's 原型繼承特性。
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:true}); console.log(o.average, o.sum); // logs 2, 6
若函式以建構子的身份呼叫(使用 new
關鍵字) this
會和被建構的新物件綁定。
備註:建構子預設透過 this
回傳該物件的參照,但它其實能回傳其他物件。如果回傳值不是物件的話,就會回傳 this
這個物件。
/* * 建構子會如此做動: * * function MyConstructor(){ * // 實際的函式主體碼在這裡 * // 在|this| 上創建屬性 * // 希望通過分配給他們,如: * this.fum = "nom"; * // et cetera... * * // 如果函式有返回狀態它將返回一個物件 * // 那個物件將是新表達式的結果。 * // 換句話來說,表達式的結果是現在綁定 |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
在上例的 C2
,由於物件在建構的時候被呼叫,新的物件 this
was bound to simply gets discarded。這也實質上令 this.a = 37;
宣告死亡:不是真的死亡(因為已經執行了),但它在毫無 outside effects 的情況下遭到消滅。
當一個函式用作事件處理器的話,this
值會設在觸發事件的元素(某些瀏覽器如果不用 addEventListener
方法的話,在動態添加監聽器時,就不會遵循這個常規)
// 當監聽器被調用,相關元素變為藍色 function bluify(e){ // 永遠是真 console.log(this === e.currentTarget); // 當當前目標和目標為相同物件為真 console.log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // 取得文件內所有的元素 var elements = document.getElementsByTagName('*'); // Add bluify as a click listener so when the // element is clicked on, it turns blue for(var i=0 ; i<elements.length ; i++){ elements[i].addEventListener('click', bluify, false); }
當程式碼從行內的 on 事件處理器呼叫的話,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 物件(例如在非嚴格模式下,呼叫函數沒設定 this
的預設物件)
規範 | 狀態 | 註解 |
---|---|---|
{{SpecName('ESDraft', '#sec-this-keyword', 'The this keyword')}} | {{Spec2('ESDraft')}} | |
{{SpecName('ES6', '#sec-this-keyword', 'The this keyword')}} | {{Spec2('ES6')}} | |
{{SpecName('ES5.1', '#sec-11.1.1', 'The this keyword')}} | {{Spec2('ES5.1')}} | |
{{SpecName('ES3', '#sec-11.1.1', 'The this keyword')}} | {{Spec2('ES3')}} | |
{{SpecName('ES1', '#sec-11.1.1', 'The this keyword')}} | {{Spec2('ES1')}} | 初始定義。在 JavaScript 1.0 導入。 |
{{Compat("javascript.operators.this")}}
this
上下文不同的相關文章