1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
---
title: this
slug: Web/JavaScript/Reference/Operators/this
tags:
- JavaScript
- this
translation_of: Web/JavaScript/Reference/Operators/this
---
<div>{{jsSidebar("Operators")}}</div>
<p>JavaScript <strong>函式內的 <code>this</code> 關鍵字</strong>表現,和其他語言相比略有差異。在<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode">嚴格模式</a>與非嚴格模式下也有所不同。</p>
<p>通常,<code>this</code> 值由被呼叫的函式來決定。它不能在執行期間被指派,每次函式呼叫調用的值也可能不同。ES5 引入了 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">bind</a></code> 方法去<a href="#The_bind_method">設置函式的 <code>this</code> 值,而不管它怎麼被呼叫。</a>ECMAScript 2015 也導入了定義 <code>this</code> 詞法範圍的<a href="../Functions/Arrow_functions">箭頭函式</a>(它的 <code>this</code> 值會維持在詞法作用域)。</p>
<div>{{EmbedInteractiveExample("pages/js/expressions-this.html")}}</div>
<h2 id="語法">語法</h2>
<pre class="syntaxbox">this</pre>
<h2 id="全域環境下">全域環境下</h2>
<p><code>this</code> 值在所有函式以外的全域執行環境下,會被當作全域物件,無論是否處於嚴格模式。</p>
<pre class="brush:js">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"
</pre>
<h2 id="函式環境下">函式環境下</h2>
<p>在函式內的 <code>this</code> 值取決於該函式如何被呼叫。</p>
<h3 id="簡易呼叫">簡易呼叫</h3>
<p>因為以下程式碼並不處於<a href="/zh-TW/docs/Web/JavaScript/Reference/Strict_mode">嚴謹模式</a>下、而 <code>this</code> 值也沒被呼叫(call)設定,<code>this</code> 會變成全域物件,在瀏覽器之下則會變成 <code>window</code>。</p>
<pre class="brush:js">function f1(){
return this;
}
//在瀏覽器中:
f1() === window; // true
//Node中:
f1() === global; // true
</pre>
<p>然而,在嚴格模式下,<code>this</code> 值會在進入執行環境時建立並維持該狀態。因此,下例的 <code>this</code> 預設值是 <code>undefined</code>:</p>
<pre class="brush:js">function f2(){
"use strict"; // 嚴格模式
return this;
}
f2() === undefined; //true</pre>
<p>所以在嚴格模式下,如果 <code>this</code> 沒有定義到執行環境內,其預設值就會是 <code>undefined</code>。</p>
<div class="note">
<p>在第二個例子裡面,<code>this</code> 應為 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/undefined"><code>undefined</code></a>,因為 <code>f2</code> 是直接被呼叫,而不是在其為某個物件的方法或屬性的情況下(例如 <code>window.f2()</code>)被直接呼叫。某些瀏覽器首次支援<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode" title="Strict mode">嚴格模式</a>時沒導入這個特徵,它們會因此錯誤的回傳 <code>window</code> 物件。</p>
</div>
<p>要從某個語境訪問另一個 <code>this</code> 語境的值,請使用 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call</a> 或 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply</a>:</p>
<pre>// 物件可以被當作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'
</pre>
<p>當函式內部調用 <code>this</code> 關鍵字時,其值會和所有繼承自 <code>Function.prototype</code> 並使用 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call</a></code> 或 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply</a></code> 方法呼叫的特定物件綁定。</p>
<pre>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</pre>
<p>使用 <code>call</code> 和 <code>apply</code> 時,如果被輸入作為 <code>this</code> 的值不是物件,JavaScript 內建的 <code>ToObject</code> 運算符會試著把被輸入的值轉變為物件。如果被輸入的值是一個原始型別,例如 <code>7</code>或是 <code>'foo'</code>,它們會自動被相關的建構方法轉變為物件。因此,原始數值<code>7</code>會轉變成類似用<code>new Number(7)</code>產生的物件,而字串<code>'foo'</code>會轉變成類似用<code>new String('foo')</code>產生的物件。</p>
<pre class="brush:js">function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call('foo'); // [Object String]
</pre>
<h3 id="bind_方法"><code>bind</code> 方法</h3>
<p>ECMAScript 5 導入了 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">Function.prototype.bind</a></code>。呼叫 <code>f.bind(someObject)</code> 會建立和 <code>f</code> 的主體與作用域相同之新函式;但無論函數怎麼被調用,原始函數的 <code>this</code> 在新函數將永遠與 <code>bind</code> 的第一個參數綁定起來。</p>
<pre><code>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</code>
</pre>
<h3 id="箭頭函式">箭頭函式</h3>
<p>在<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions">箭頭函式</a>下,<code>this</code> 值保留了其在詞法作用域 的 <code>this</code> 值。在全域程式碼內,則設為全域物件:</p>
<pre class="brush: js">var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true</pre>
<div class="note">
<p>註:如果這參數被傳遞給箭頭函式的 call, bind, apply 調用,該參數會被忽略。你仍然可以將參數預先調用到call,但第一個參數(thisArg)必須設置為空。</p>
</div>
<pre class="brush: js">// 作為物件的方法呼叫
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</pre>
<p>無論以上哪種,<code>foo</code> 的 <code>this</code> 在建立的時候,都會設為原本的樣子(以上面的例子來說,就是全域物件)。這同樣適用於在其他函式內創建的箭頭函式:它們的 <code>this</code> 是設置為外部執行上下文。</p>
<pre class="brush: js">// 建立一個物件,其方法 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</pre>
<p>以上面的程式碼為例,稱作匿名函數(anonymous function)A 的函數被指定為 <code>obj.bar</code>,它回傳的函數(稱作匿名函數 B)作為箭頭函數而建立。因而,函數 B 的 <code>this</code> 在呼叫時,將永遠設為 <code>obj.bar</code> (函數 A)的 <code>this</code>。呼叫被回傳的函數(函數 B)時,它的 <code>this</code> 將一直是原本的樣子。</p>
<p>再以上面的程式碼為例,函數 B 的 <code>this</code> 被設為函數 A 的 <code>this</code>,而它屬於物件,所以它依然會設為 <code>obj</code>,就算在 <code>this</code> 設為 <code>undefined</code> 或全域物件的呼叫方式下(或在全域執行環境下,上例的任何方法)</p>
<h3 id="作為物件方法">作為物件方法</h3>
<p>如果一個函式是以物件的方法呼叫,它的 <code>this</code> 會設為該呼叫函式的物件。</p>
<p>以下面的程式碼為例,呼叫 <code>o.f()</code> 的時候,函式內的 <code>this</code> 會和 <code>o</code> 物件綁定。</p>
<pre class="brush:js">var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
</pre>
<p>請注意這個行為,不會受函式如何或何處定義所影響。以上面為例,在我們定義 <code>o</code> 時,也定義了行內函式 <code>f</code> 作為構件(member)。不過,我們也能先定義函式,接著讓它附屬到 <code>o.f</code>。這麼做會得出相同的效果:</p>
<pre class="brush:js">var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
</pre>
<p>這表明了 <code>this</code> 只和 <code>f</code> 作為 <code>o</code> 的構件呼叫有所關聯。</p>
<p>同樣的,<code>this</code> 綁定只會受最直接的構件引用(most immediate member reference)所影響。在下面的例子,我們將物件 <code>o.b</code> 的方法 <code>g</code> 作為函式呼叫。在執行的期間,函式內的 <code>this</code> 會參照 <code>o.b</code>。物件是否為 <code>o</code> 的構件無關緊要,最直接的引用才是最緊要的。</p>
<pre class="brush:js">o.b = {g: independent, prop: 42};
console.log(o.b.g()); // logs 42
</pre>
<h4 id="物件原型鏈上的_this">物件原型鏈上的 <code>this</code></h4>
<p>相同概念也能套用定義物件原型鏈的方法。如果方法放在物件的原型鏈上,<code>this</code> 會指向方法所呼叫的物件,如同該方法在物件上的樣子。</p>
<pre class="brush:js">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
</pre>
<p>在這個示例中,分配給變數<code>p</code> 的物件沒有自己的 <code>f</code> 屬性, 它(p)繼承了它(o)的原型。但是查找 <code>f</code> 最終在 <code>o</code>上找到它的成員名為 f 並不重要。查找開始作為 <code>p.f</code>的引用,所以 <code>this</code> 在函式內部物件的值被當作是<code>p</code>的引用。也就是說,<code>f</code> 作為 <code>p</code>的調用方法以來, 它的 <code>this</code> 指的就是 <code>p</code>. 這是一個非常有趣的JavaScript's 原型繼承特性。</p>
<h4 id="帶著_getter_或_setter_的_this">帶著 getter 或 setter 的 <code>this</code></h4>
<p>當函式從 getter 或 setter被調用的時候,同樣的概念也成立。用作 getter 或setter 的函式將自己的 <code>this</code> 綁定到從中設置或獲取的物件上。</p>
<pre class="brush:js">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
</pre>
<h3 id="作為建構子">作為建構子</h3>
<p>若函式以建構子的身份呼叫(使用 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/new">new</a></code> 關鍵字) <code>this</code> 會和被建構的新物件綁定。</p>
<div class="note">
<p>建構子預設透過 <code>this</code> 回傳該物件的參照,但它其實能回傳其他物件。如果回傳值不是物件的話,就會回傳 <code>this</code> 這個物件。</p>
</div>
<pre class="brush:js">/*
* 建構子會如此做動:
*
* 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
</pre>
<p>在上例的 <code>C2</code>,由於物件在建構的時候被呼叫,新的物件 <code>this</code> was bound to simply gets discarded。這也實質上令 <code>this.a = 37;</code> 宣告死亡:不是真的死亡(因為已經執行了),但它在毫無 outside effects 的情況下遭到消滅。</p>
<h3 id="作為_DOM_事件處理器">作為 DOM 事件處理器</h3>
<p>當一個函式用作事件處理器的話,<code>this</code> 值會設在觸發事件的元素(某些瀏覽器如果不用 <code>addEventListener</code> 方法的話,在動態添加監聽器時,就不會遵循這個常規)</p>
<pre class="brush:js">// 當監聽器被調用,相關元素變為藍色
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);
}</pre>
<h3 id="作為行內事件處理器">作為行內事件處理器</h3>
<p>當程式碼從行內的<a href="/zh-TW/docs/Web/Guide/Events/Event_handlers"> on 事件處理器</a>呼叫的話,<code>this</code> 就會設在監聽器所置的 DOM 元素:</p>
<pre class="brush:js"><button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
</pre>
<p>上方的 alert 會秀出 <code>button</code>。但請注意只有外層程式碼的 <code>this</code> 要這樣設定:</p>
<pre class="brush:js"><button onclick="alert((function(){return this})());">
Show inner this
</button>
</pre>
<p>在這裡,內部函式的並沒有設立 <code>this</code>,所以它會回傳全域/window 物件(例如在非嚴格模式下,呼叫函數沒設定 <code>this</code> 的預設物件)</p>
<h2 id="規範">規範</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">規範</th>
<th scope="col">狀態</th>
<th scope="col">註解</th>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-this-keyword', 'The this keyword')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td> </td>
</tr>
<tr>
<td>{{SpecName('ES6', '#sec-this-keyword', 'The this keyword')}}</td>
<td>{{Spec2('ES6')}}</td>
<td> </td>
</tr>
<tr>
<td>{{SpecName('ES5.1', '#sec-11.1.1', 'The this keyword')}}</td>
<td>{{Spec2('ES5.1')}}</td>
<td> </td>
</tr>
<tr>
<td>{{SpecName('ES3', '#sec-11.1.1', 'The this keyword')}}</td>
<td>{{Spec2('ES3')}}</td>
<td> </td>
</tr>
<tr>
<td>{{SpecName('ES1', '#sec-11.1.1', 'The this keyword')}}</td>
<td>{{Spec2('ES1')}}</td>
<td>初始定義。在 JavaScript 1.0 導入。</td>
</tr>
</tbody>
</table>
<h2 id="瀏覽器相容性">瀏覽器相容性</h2>
<p>{{Compat("javascript.operators.this")}}</p>
<h2 id="參見">參見</h2>
<ul>
<li><a href="/zh-TW/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode">嚴格模式</a></li>
<li><a href="http://bjorn.tipling.com/all-this">All this</a>,一篇關於 <code>this</code> 上下文不同的相關文章</li>
<li><a href="http://rainsoft.io/gentle-explanation-of-this-in-javascript/">親和地解釋 JavaScript 的「this」關鍵字</a></li>
</ul>
|