diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:43:23 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:43:23 -0500 |
| commit | 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 (patch) | |
| tree | a9ef8ac1e1b8fe4207b6d64d3841bfb8990b6fd0 /files/zh-tw/web/javascript/reference/operators/this/index.html | |
| parent | 074785cea106179cb3305637055ab0a009ca74f2 (diff) | |
| download | translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.gz translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.tar.bz2 translated-content-218934fa2ed1c702a6d3923d2aa2cc6b43c48684.zip | |
initial commit
Diffstat (limited to 'files/zh-tw/web/javascript/reference/operators/this/index.html')
| -rw-r--r-- | files/zh-tw/web/javascript/reference/operators/this/index.html | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/files/zh-tw/web/javascript/reference/operators/this/index.html b/files/zh-tw/web/javascript/reference/operators/this/index.html new file mode 100644 index 0000000000..e26b592244 --- /dev/null +++ b/files/zh-tw/web/javascript/reference/operators/this/index.html @@ -0,0 +1,385 @@ +--- +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 dir="rtl">// 物件可以被當作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> + +<div class="hidden">此頁面上的兼容性數據生成自結構化數據。如果你想為數據做出貢獻,請產看<a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> 並且向我們發送拉取請求。</div> + +<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> |
