aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/web/javascript/reference/functions/arrow_functions/index.html
blob: 0b53f14444fdee5190d8a2bee03f573030f1a535 (plain)
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
---
title: 箭頭函式
slug: Web/JavaScript/Reference/Functions/Arrow_functions
translation_of: Web/JavaScript/Reference/Functions/Arrow_functions
---
<div>{{jsSidebar("Functions")}}</div>

<p><strong>箭頭函式運算式</strong>(arrow function expression)擁有比<a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/function">函式運算式</a>還簡短的語法。它沒有自己的 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/this">this</a></code><a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/arguments">arguments</a><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/super">super</a><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/new.target">new.target</a> 等語法。本函式運算式適用於非方法的函式,但不能被用作建構式(constructor)。</p>

<div>{{EmbedInteractiveExample("pages/js/functions-arrow.html")}}</div>

<h2 id="基本語法">基本語法</h2>

<pre class="syntaxbox">(參數1, 參數2, …, 參數N) =&gt; { 陳述式; }

(參數1, 參數2, …, 參數N) =&gt; 表示式;
// 等相同(參數1, 參數2, …, 參數N) =&gt; { return 表示式; }

// 只有一個參數時,括號才能不加:
(單一參數) =&gt; { 陳述式; }
單一參數 =&gt; { 陳述式; }

//若無參數,就一定要加括號:
() =&gt; { statements }
</pre>

<h2 id="進階語法">進階語法</h2>

<pre class="syntaxbox">// 用大括號將內容括起來,返回一個物件字面值表示法:
params =&gt; ({foo: bar})

// 支援<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/rest_parameters">其餘參數</a><a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/Default_parameters">預設參數</a>
(param1, param2, ...rest) =&gt; { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) =&gt; {
statements }

// 也支援 parameter list 的<a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">解構</a>
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) =&gt; a + b + c; f(); // 6
</pre>

<h2 id="說明">說明</h2>

<p>也可參閱 <a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">"ES6 In Depth: Arrow functions" on hacks.mozilla.org</a></p>

<p>箭頭函式有兩個重要的特性:更短的函式寫法與 <code>this</code> 變數的非綁定。</p>

<h3 id="更短的函式寫法">更短的函式寫法</h3>

<pre class="brush: js">var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

// 這段函式會輸出[8, 6, 7, 9]這個陣列
elements.<a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/map">map</a>(function(element) {
  return element.length;
});

// 上方這種一般的函式,可以被改寫成下方的箭頭函式
elements.map((element) =&gt; {
  return element.length;
}); // [8, 6, 7, 9]

// 如果輸入的參數只有一個,我們可以移除掉外面的括號
elements.map(element =&gt; {
  return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式裡的內容只有'return'的時候,我們可以拿掉return和外面的大括號
elements.map(element =&gt; element.length); // [8, 6, 7, 9]

// 在這個範例中,因為我們只需要length這個屬性,所以也可以使用<a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">解構賦值</a>:
// 下方的'length'對應到我們想取得的屬性,而'lengthFooBArX'只是很普通的變數名稱,
// 可以被任意修改成你想要的名字
elements.map(({ length: lengthFooBArX }) =&gt; lengthFooBArX); // [8, 6, 7, 9]

// 上面這種解構賦值之後的參數也可以被改寫為下面這樣。但要注意的是,在這個範例中,
// 我們不是要指定'length'這個值給一個虛構的屬性,而是這個變數的名稱'length'本身就是
// 用來當成我們想從物件上取得的屬性
elements.map(({ length }) =&gt; length); // [8, 6, 7, 9]
</pre>

<h3 id="this_不分家"><code>this</code> 不分家</h3>

<p>在有箭頭函數之前,每個新函式是依據如何被呼叫來定義自己的 <code><a href="/zh-TW/docs/Web/JavaScript/Reference/Operators/this">this</a></code> 變數<br>
 例如:</p>

<ul>
 <li>在建構子時是一個新物件</li>
 <li>在呼叫<a href="/zh-TW/docs/Web/JavaScript/Reference/Strict_mode">嚴格模式</a>函數時是 undefined</li>
 <li>以物件方法呼叫時則為基礎物件</li>
 <li>等等....</li>
</ul>

<p>事實證明這對物件導向程式設計來說並不理想。</p>

<pre class="brush: js">function Person() {
  // Person() 建構式將 this 定義為它自己的一個實體
  this.age = 0;

  setInterval(function growUp() {
    // 在非嚴格模式下, growUp() 函式把 this 定義為全域物件
    // (因為那是 growUp()執行的所在),
    // 與 Person() 建構式所定義的 this 有所不同
    this.age++;
  }, 1000);
}

var p = new Person();</pre>

<p>在 ECMAScript 3/5 裡面,有關 <code>this</code> 的問題,可以透過指派 <code>this</code> 值給可以關閉的變數解決。</p>

<pre class="brush: js">function Person() {
  var self = this; // 有些人喜歡 `that` 而不是 `self`.
                   // 選好一種取法後始終如一
  self.age = 0;

  setInterval(function growUp() {
    // 這個 callback 參考 `self` 變數,為預期中的物件。
    self.age++;
  }, 1000);
}</pre>

<p>或者透過 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">bind</a> 函式來綁定 <code>this</code> 變數到指定函式(以上面為例,就是 <code>growUp()</code> 函式)。</p>

<p>箭頭函式並不擁有自己的 <code>this 變</code><code></code>使用的 this <code>值來自</code>封閉的文本上下文,也就是說,箭頭函式遵循常規變量查找規則。因此,如果在當前範圍中搜索不到 this 變量時,他們最終會尋找其封閉範圍。</p>

<p>因此,在以下程式碼內,傳遞給 <code>setInterval</code> 的 箭頭函式中的<code>this</code> ,會與封閉函式的 <code>this</code> 值相同:</p>

<pre class="brush: js">function Person(){
  this.age = 0;

  setInterval(() =&gt; {
    this.age++; // |this| 適切的參考了Person建構式所建立的物件
  }, 1000);
}

var p = new Person();</pre>

<h4 id="和嚴格模式的關係">和嚴格模式的關係</h4>

<p>由於 <code>this</code> 變數具有詞彙上綁定意義,所以<a href="/zh-TW/docs/Web/JavaScript/Reference/Strict_mode">嚴格模式</a>的宣告對 <code>this</code> 的作用將被忽略。</p>

<pre class="brush: js">var f = () =&gt; {'use strict'; return this};
f() === window; // 或是 global 物件</pre>

<p>但嚴格模式的其他作用依舊存在。</p>

<h4 id="由_call_與_apply_函式呼叫">由 call 與 apply 函式呼叫</h4>

<p>由於箭頭函式並沒有自己的 <code>this</code>,所以透過 <code>call()</code><code>apply()</code> 呼叫箭頭函式只能傳入參數。<code>thisArg</code> 將會被忽略。</p>

<pre class="brush: js">var adder = {
  base : 1,
  add : function(a) {
    var f = v =&gt; v + this.base;
    return f(a);
  },
  addThruCall: function(a) {
    var f = v =&gt; v + this.base;
    var b = {
      base : 2
    };
    return f.call(b, a);
  }
};
console.log(adder.add(1));         // 顯示 2
console.log(adder.addThruCall(1)); // 依舊顯示 2
</pre>

<h3 id="不綁定_arguments">不綁定 <code>arguments</code></h3>

<p>箭頭函式並沒有自己的 <a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/arguments"><code>arguments</code> 物件</a>。所以在此例中,<code>arguments</code> 只是參考到 enclosing 作用域裡面的相同變數:</p>

<pre class="brush: js">var arguments = [1, 2, 3];
var arr = () =&gt; arguments[0];

arr(); // 1

function foo(n) {
  var f = () =&gt; arguments[0] + n; // <em>foo</em>'s implicit arguments binding. arguments[0] is n
  return f();
}

foo(1); // 2</pre>

<p>大多時候,使用<a href="/zh-TW/docs/Web/JavaScript/Reference/Functions/rest_parameters">其餘參數</a> 是取代 <code>arguments</code> 物件的較好方式。</p>

<pre class="brush: js">function foo(n) {
  var f = (...args) =&gt; args[0] + n;
  return f(10);
}

foo(1); // 11</pre>

<h3 id="將箭頭函式撰寫為方法">將箭頭函式撰寫為方法</h3>

<p>如同前述,箭頭函式運算式最適合用在非方法的函式。來看看如果要把它們當成方法來用,會發生什麼事:</p>

<pre class="brush: js"><code>'use strict';
var obj = {
  i: 10,
  b: () =&gt; console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
}
obj.b(); // 印出 undefined, Window {...} (or the global object)
obj.c(); // 印出 10, Object {...}</code></pre>

<p>箭頭函式並沒有自己的 <code>this</code>。另一個例子與 {{jsxref("Object.defineProperty()")}} 有關:</p>

<pre class="brush: js">'use strict';

var obj = {
  a: 10
};

Object.defineProperty(obj, 'b', {
  get: () =&gt; {
    console.log(this.a, typeof this.a, this); // undefined, 'undefined', Window {...} (or the global object)
    return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
  }
});
</pre>

<h3 id="使用_new_運算子">使用 <code>new</code> 運算子</h3>

<p>箭頭函式不可作為建構式使用;若使用於建構式,會在使用 <code>new</code> 時候拋出錯誤。</p>

<pre class="brush: js">var Foo = () =&gt; {};
var foo = new Foo(); // TypeError: Foo is not a constructor</pre>

<h3 id="使用_prototype_屬性">使用 <code>prototype</code> 屬性</h3>

<p>箭頭函式並沒有原型(<code>prototype</code>)屬性。</p>

<pre class="brush: js">var Foo = () =&gt; {};
console.log(Foo.prototype); // undefined
</pre>

<h3 id="使用關鍵字_yield">使用關鍵字 <code>yield</code></h3>

<p><code><a href="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/yield">yield</a></code> 關鍵字無法用於箭頭函式的 body(except when permitted within functions further nested within it)。因此,箭頭函式無法使用 generator。</p>

<h2 id="函式主體(Function_body)">函式主體(Function body)</h2>

<p>箭頭函式可以變成 concise body 或者平常使用的 block body。</p>

<p>在 concise body 裡面只需要輸入運算式,就會附上內建的回傳。在 block body 裡面就必須附上明確的 <code>return</code> 宣告。</p>

<pre class="brush: js"><code>var func = x =&gt; x * x;                  // concise 語法會內建 "return"
var func = (x, y) =&gt; { return x + y; }; // block body 需要明確的 "return"</code></pre>

<h2 id="回傳物件字面值">回傳物件字面值</h2>

<p>請注意只使用 <code>params =&gt; {object:literal}</code> 並不會按照期望般回傳物件字面值(object literal)。</p>

<pre class="brush: js"><code>var func = () =&gt; { foo: 1 };               // Calling func() returns undefined!
var func = () =&gt; { foo: function() {} };   // SyntaxError: Unexpected token (</code></pre>

<p>因為在大括弧(<code>{}</code>)裡面的文字會被解析為有序宣告(例如 <code>foo</code> 會被當作標記(label)、而不是物件的 key )</p>

<p>要記得把物件字面值包在圓括弧內。</p>

<pre class="brush: js"><code>var func = () =&gt; ({foo: 1});
var func = () =&gt; ({ foo: function() {} }); </code>
</pre>

<h2 id="換行">換行</h2>

<p>箭頭函式不可以在參數及箭頭間包含換行。</p>

<pre class="brush: js"><code>var func = ()
           =&gt; 1; // SyntaxError: expected expression, got '=&gt;'</code></pre>

<h2 id="Parsing_order">Parsing order</h2>

<p>箭頭函式的箭頭儘管不是操作符,但藉著<a href="https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Operator_Precedence">運算子優先等級</a>,箭頭函式有著和普通函式不相同的特殊解析規則。</p>

<pre class="brush: js"><code>let callback;

callback = callback || function() {}; // ok
callback = callback || () =&gt; {};      // SyntaxError: invalid arrow-function arguments
callback = callback || (() =&gt; {});    // ok</code></pre>

<h2 id="更多範例">更多範例</h2>

<pre class="brush: js">// 回傳 undefined 的箭頭函式
let empty = () =&gt; {};

(() =&gt; "foobar")() // 回傳 "foobar"

var simple = a =&gt; a &gt; 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

let max = (a, b) =&gt; a &gt; b ? a : b;

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) =&gt; a + b);  // 66
var even = arr.filter(v =&gt; v % 2 == 0); // [6, 0, 18]
var double = arr.map(v =&gt; v * 2);       // [10, 12, 26, 0, 2, 36, 46]

</pre>

<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('ES6', '#sec-arrow-function-definitions', 'Arrow Function Definitions')}}</td>
   <td>{{Spec2('ES6')}}</td>
   <td>Initial definition.</td>
  </tr>
 </tbody>
</table>

<h2 id="瀏覽器相容性">瀏覽器相容性</h2>

<div>


<p>{{Compat("javascript.functions.arrow_functions")}}</p>
</div>

<h2 id="參閱">參閱</h2>

<ul>
 <li><a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">"ES6 In Depth: Arrow functions" on hacks.mozilla.org</a></li>
</ul>