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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
---
title: Hàm mũi tên (hàm rút gọn)
slug: Web/JavaScript/Reference/Functions/Arrow_functions
translation_of: Web/JavaScript/Reference/Functions/Arrow_functions
---
<div>{{jsSidebar("Functions")}}</div>
<p><strong>Biểu thức hàm mũi tên</strong> là một thay thế rút gọn cho <a href="/en-US/docs/Web/JavaScript/Reference/Operators/function">hàm biểu thức</a> truyền thống, nhưng bị hạn chế và không thể sử dụng trong mọi trường hợp.</p>
<p><strong>Sự khác biệt & Hạn chế:</strong></p>
<ul>
<li>Không hỗ trợ binddings đến con trỏ <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Operators/this">this</a></code> hoặc <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Operators/super">super</a></code>, và không nên dùng ở <code><a href="https://wiki.developer.mozilla.org/vi/docs/Glossary/Method">methods</a></code>.</li>
<li>Không có <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Functions/arguments">arguments</a></code>, hoặc từ khóa <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Operators/new.target">new.target</a></code>.</li>
<li>Không phù hợp với các phương thức <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call</a></code>, <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply</a></code> và <a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Function/bind"><code>bind</code></a>, thường dựa vào thiết lập <a href="https://wiki.developer.mozilla.org/vi/docs/Glossary/Scope"><code>scope</code>.</a></li>
<li>Không sử dụng để <code><a href="https://wiki.developer.mozilla.org/vi/docs/Glossary/constructor">constructors</a></code>.</li>
<li>Không thể dùng <code><a href="https://wiki.developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Operators/yield">yield</a></code>, trong nội dung (body).</li>
</ul>
<p>{{EmbedInteractiveExample("pages/js/functions-arrow.html")}}</p>
<h3 id="So_sánh_hàm_truyền_thống_và_hàm_mũi_tên"><strong>So sánh hàm truyền thống và hàm mũi tên</strong></h3>
<p>Hãy phân tách "hàm truyền thống" thành "hàm mũi tên" đơn giản nhất theo từng bước:<br>
LƯU Ý: Mỗi bước là một "hàm mũi tên" hợp lệ.</p>
<pre class="notranslate">// Hàm truyền thống
function (a){
return a + 100;
}
// Phân rã thành hàm mũi tên
// 1. Xóa từ khóa "function" và thay thế bằng mũi tên ở giữa đối số và dấu ngoặc nhọn bắt đầu nội dung hàm
(a) => {
return a + 100;
}
// 2. Xóa dấu ngoặc nhọn và từ khóa "return" sự trả về đã bao hàm (mặc định) khi sử dụng hàm mũi tên.
(a) => a + 100;
// 3. Xóa luôn dấu ngoặc đơn của đối số
a => a + 100;</pre>
<p>Như bạn thấy ở bên trên, dấu { ngoặc nhọn } và dấu (ngoặc tròn ở đối ố) và "return" là tùy chọn, nhưng đôi khi có thể bắt buộc phải có.</p>
<p>Ví dụ, nếu bạn có <strong>nhiều đối số</strong> hoặc <strong>không có đối số</strong>, bạn cần phải thêm dấu ngoặc tròn vào xung quanh các đối số:</p>
<pre class="notranslate">// Hàm Truyền thống
function (a, b){
return a + b + 100;
}
// Hàm mũi tên
(a, b) => a + b + 100;
// Hàm truyền thống (không đối số)
let a = 4;
let b = 2;
function (){
return a + b + 100;
}
// Hàm mũi tên (không đối số)
let a = 4;
let b = 2;
() => a + b + 100;</pre>
<p>Tương tự như vậy, nếu nội dung (body) hàm cần thêm <strong>nhiều dòng</strong> để xử lý thì bạn cần thêm vào dấu ngoặc nhọn <strong>CỘNG thêm "return"</strong> (hàm mũi tên không có kỳ diệu đến mức biết khi nào bạn muốn "return"):</p>
<pre class="notranslate">// Hàm truyền thống
function (a, b){
let chuck = 42;
return a + b + chuck;
}
// Hàm mũi tên
(a, b) => {
let chuck = 42;
return a + b + chuck;
}</pre>
<p>Và cuối cùng, với các <strong>hàm được đặt tên</strong> chúng tôi xử lý các biểu thức mũi tên như các biến</p>
<pre class="notranslate">// Hàm truyền thống
function bob (a){
return a + 100;
}
// Hàm mũi tên
let bob = a => a + 100;</pre>
<h2 id="Cú_pháp">Cú pháp</h2>
<h3 id="Cú_pháp_cơ_bản">Cú pháp cơ bản</h3>
<pre class="syntaxbox notranslate"><strong>(</strong><em>param1</em>, <em>param2</em>, …, <em>paramN</em><strong>) => {</strong> <em>statements</em> <strong>}</strong>
<strong>(</strong><em>param1</em>, <em>param2</em>, …, <em>paramN</em><strong>) =></strong> <em>expression</em>
// tương đương với: <strong>(</strong><em>param1</em>, <em>param2</em>, …, <em>paramN</em><strong>)</strong> => { return <em>expression</em>; }
// Dấu ngoặc đơn không bắt buộc khi chỉ có một tham số truyền vào:
<em>(singleParam)</em> <strong>=> {</strong> <em>statements</em> <strong>}</strong>
<em>singleParam</em> <strong>=></strong> { <em>statements }</em>
// Hàm khi không có tham số truyền vào bắt buộc phải là dấu ():
<strong>() => {</strong> <em>statements</em> <strong>}
</strong>() => <em>expression</em> // tương đương: () => { return <em>expression</em>; }
</pre>
<h3 id="Cú_pháp_nâng_cao">Cú pháp nâng cao</h3>
<pre class="syntaxbox notranslate">// Bên trong dấu ngoặc đơn là một đối tượng:
<em>params</em> => ({<em>foo: bar</em>})
// <a href="/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">Rest parameters</a> và <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">default parameters</a> được hỗ trợ
(<em>param1</em>, <em>param2</em>, <strong>...rest</strong>) => { <em>statements</em> }
(<em>param1</em> <strong>= defaultValue1</strong>, <em>param2</em>, …, paramN <strong>= defaultValueN</strong>) => { <em>statements</em> }
// <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring</a> within the parameter list is also supported
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
</pre>
<p>Chi tiết các ví dụ bạn có thể xem ở <a href="http://wiki.ecmascript.org/doku.php?id=harmony:arrow_function_syntax">đây</a>.</p>
<h2 id="Mô_tả">Mô tả</h2>
<p>Xem thêm <a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">"ES6 Chuyên sâu: Những hàm arrow" trên hacks.mozilla.org</a>.</p>
<p>Two factors influenced the introduction of arrow functions: shorter functions and non-binding of <code>this</code>.</p>
<h3 id="Hàm_ngắn">Hàm ngắn</h3>
<p>Một vài ví dụ, cú pháp hàm rút gọn luôn được <strong>coder </strong>yêu thích, cùng so sánh:</p>
<pre class="brush: js notranslate">var materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
// thông thường
var materialsLength1 = materials.map(function(material) {
return material.length;
});
// ngắn hơn (như mùa đông 5 độ vậy)
var materialsLength2 = materials.map((material) => {
return material.length;
});
// ngắn hơn nữa (và -2 độ, bạn còn bao nhiêu cm ?)
var materialsLength3 = materials.map(material => material.length);
</pre>
<h3 id="Không_ràng_buộc_this">Không ràng buộc <code>this</code></h3>
<p>Cho tới khi hàm rút gọn xuất hiện, mọi hàm mới đều tự định nghĩa giá trị <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/this">this</a></code> của riêng hàm (là object vừa được khởi tạo nếu dùng constructor, là undefined nếu đặt <a href="/en-US/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a> khi gọi hàm, là context object nếu hàm được gọi như một "object method", vân vân.). <code>This</code> trở nên khá khó chịu khi làm việc với phong cách lập trình hướng đối tượng.</p>
<pre class="brush: js notranslate">function Person() {
// constructor của Person() định nghĩa `this` như một biến.
this.age = 0;
setInterval(function growUp() {
// Khi dùng non-strict mode, hàm growUp() định nghĩa `this`
// như một đối tượng toàn cục, khác hoàn toàn so với `this`
// được định nghĩa bởi constructor của Person().
this.age++;
}, 1000);
}
var p = new Person();</pre>
<p>Theo ECMAScript 3/5, vấn đề của <code>this</code> có thể sửa được bằng cách gán <code>this</code> cho một biến gần nhất.</p>
<pre class="brush: js notranslate">function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// Hàm callback trỏ tới biến `that`với
// giá trị là đối tượng mong đợi.
that.age++;
}, 1000);
}</pre>
<p>Nói cách khác, tạo ra một <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">hàm ràng buộc</a> để truyền giá trị của <code>this</code> vào hàm ràng buộc đích (chẳng hạn như hàm <code>growUp()</code> phía trên).</p>
<p>Hàm rút gọn không tạo ra ngữ cảnh <code>this</code> của riêng hàm, thế nên <code>this</code> có ý nghĩa trong ngữ cảnh bọc quanh nó. Đoạn code phía dưới là một ví dụ:</p>
<pre class="brush: js notranslate">function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| ở đây trỏ tới đối tượng person
}, 1000);
}
var p = new Person();</pre>
<h4 id="Mối_liên_hệ_với_strict_mode">Mối liên hệ với strict mode</h4>
<p>Giả sử <code>this</code> bị bó buộc trong thân hàm, <a href="/en-US/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a> sẽ khiến cho <code>this</code> bị bỏ qua.</p>
<pre class="brush: js notranslate">var f = () => { 'use strict'; return this; };
f() === window; // hoặc đối tượng toàn cục</pre>
<p>Các strict mode khác được áp dụng như bình thường.</p>
<h4 id="Gọi_thông_qua_call_hoặc_apply">Gọi thông qua call hoặc apply</h4>
<p>Vì <code>this</code><em> </em>không bị ràng buộc bên trong hàm rút gọn, các phương thức <code>call()</code><em> </em>hoặc <code>apply()</code> chỉ có thể truyền tham số. <code>this</code> bị bỏ qua.</p>
<pre class="brush: js notranslate">var adder = {
base: 1,
add: function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base: 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // Sẽ trả ra 2
console.log(adder.addThruCall(1)); // Vẫn sẽ trả ra 2</pre>
<h3 id="Không_ràng_buộc_arguments">Không ràng buộc <code>arguments</code></h3>
<p>Hàm rút gọn không ràng buộc <a href="/en-US/docs/Web/JavaScript/Reference/Functions/arguments"><code>arguments</code> object</a>. Do đó, trong ví dụ sau, <code>arguments</code> chỉ đơn giản là một tham chiếu đến đối tượng cùng tên trong phạm vi bao quanh:</p>
<pre class="brush: js notranslate">var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
var f = (i) => arguments[0] + i; // <em>foo</em>'s implicit arguments binding
return f(2);
}
foo(1); // 3</pre>
<p>Trong nhiều trường hợp, sử dụng <a href="/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest parameters</a> là một cách thay thế tốt để dùng đối tượng <code>arguments</code>.</p>
<pre class="brush: js notranslate">function foo() {
var f = (...args) => args[0];
return f(2);
}
foo(1); // 2</pre>
<h3 id="Dùng_hàm_rút_gọn_như_một_phương_thức">Dùng hàm rút gọn như một phương thức</h3>
<p>Như đã nói phía trên, biểu thức hàm rút gọn cực kì hợp với các hàm non-method. Hãy xem chuyện gì sẽ xảy ra khi ta dùng chúng như phương thức trong ví dụ bên dưới nhé:</p>
<pre class="brush: js notranslate">'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
}
obj.b(); // in ra undefined, Object {...}
obj.c(); // in ra 10, Object {...}</pre>
<p>Hàm rút gọn không định nghĩa ("ràng buộc") <code>this</code> của hàm. Một ví dụ khác đối với {{jsxref("Object.defineProperty()")}}:</p>
<pre class="brush: js notranslate">'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, 'b', {
get: () => {
console.log(this.a, typeof this.a, this);
return this.a + 10; // đại diện cho đối tượng toàn cục 'Window', bởi vậy 'this.a' trả về 'undefined'
}
});
</pre>
<h3 id="Dùng_toán_tử_new">Dùng toán tử <code>new</code></h3>
<p>Hàm rút gọn không thể dùng như phương thức khởi tạo và sẽ báo lỗi nếu dùng toán tử <code>new</code>.</p>
<pre class="brush: js notranslate">var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor</pre>
<h3 id="Dùng_thuộc_tính_prototype">Dùng thuộc tính <code>prototype</code></h3>
<p>Hàm rút gọn không có thuộc tính <code>prototype</code>.</p>
<pre class="brush: js notranslate">var Foo = () => {};
console.log(Foo.prototype); // undefined
</pre>
<h3 id="Dùng_từ_khoá_yield">Dùng từ khoá <code>yield</code></h3>
<p>Từ khoá <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/yield">yield</a></code> có thể sẽ không dùng được trong thân hàm rút gọn (trừ khi được gọi trong hàm lồng trong hàm rút gọn). Tức là, hàm rút gọn không thể dùng như là generator (hàm sinh).</p>
<h2 id="Phần_thân_hàm">Phần thân hàm</h2>
<p>Hàm rút gọn vừa có thể có "concise body" hoặc dạng thường thấy "block body".</p>
<p>Trong concise body, chỉ cần biểu thức, return sẽ được gán ngầm. Còn với block body, bạn phải có <code>return</code>.</p>
<pre class="brush: js notranslate">var func = x => x * x; // concise syntax, implied "return"
var func = (x, y) => { return x + y; }; // with block body, explicit "return" needed
</pre>
<h2 id="Trả_về_object_literals">Trả về object literals</h2>
<p>Không thể dùng cú pháp <code>params => {object:literal}</code> nếu muốn trả về object literal.</p>
<pre class="brush: js notranslate">var func = () => { foo: 1 }; // Calling func() returns undefined!
var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name</pre>
<p>Bởi vì đoạn code bên trong ({}) được phân giải thành một chuỗi các trình tự nối tiếp (ví dụ <code>foo</code> được coi như một nhãn, thay vì một key trong object literal).</p>
<p>Thế nên hãy bao object literal trong ngoặc tròn.</p>
<pre class="brush: js notranslate">var func = () => ({foo: 1});</pre>
<h2 id="Kí_tự_xuống_dòng">Kí tự xuống dòng</h2>
<p>Hàm rút gọn không thể chứa bất cứ kí tự rút gọn nào giữa phần truyền tham số và dấu mũi tên.</p>
<pre class="brush: js notranslate">var func = ()
=> 1; // SyntaxError: expected expression, got '=>'</pre>
<h2 id="Parsing_order">Parsing order</h2>
<p>Although the arrow in an arrow function is not an operator, arrow functions have special parsing rules that interact differently with <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence">operator precedence</a> compared to regular functions.</p>
<pre class="brush: js notranslate">let callback;
callback = callback || function() {}; // ok
callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments
callback = callback || (() => {}); // ok
</pre>
<h2 id="Một_số_ví_dụ_khác">Một số ví dụ khác</h2>
<pre class="brush: js notranslate">// An empty arrow function returns undefined
let empty = () => {};
(() => 'foobar')(); // <a href="/en-US/docs/Glossary/IIFE">IIFE</a>, returns "foobar"
var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10
let max = (a, b) => a > b ? a : b;
// Easy array filtering, mapping, ...
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b); // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46]
// More concise promise chains
promise.then(a => {
// ...
}).then(b => {
// ...
});
// Parameterless arrow functions that are visually easier to parse
setTimeout( () => {
console.log('I happen sooner');
setTimeout( () => {
// deeper code
console.log('I happen later');
}, 1);
}, 1);
</pre>
<h2 id="Đặc_điểm_kĩ_thuật">Đặc điểm kĩ thuật</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName('ES2015', '#sec-arrow-function-definitions', 'Arrow Function Definitions')}}</td>
<td>{{Spec2('ES2015')}}</td>
<td>Initial definition.</td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-arrow-function-definitions', 'Arrow Function Definitions')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="Tương_thích_trình_duyệt">Tương thích trình duyệt</h2>
<div>{{Compat("javascript.functions.arrow_functions")}}</div>
<h2 id="Firefox-specific_notes">Firefox-specific notes</h2>
<ul>
<li>The initial implementation of arrow functions in Firefox made them automatically strict. This has been changed as of <a href="/en-US/docs/Mozilla/Firefox/Releases/24">Firefox 24</a>. The use of <code>"use strict";</code> is now required.</li>
<li>Arrow functions are semantically different from the non-standard {{jsxref("Operators/Expression_Closures", "expression closures", "", 1)}} added in <a href="/en-US/Firefox/Releases/3">Firefox 3</a> (details: <a href="/en-US/docs/Web/JavaScript/New_in_JavaScript/1.8">JavaScript 1.8</a>), for {{jsxref("Operators/Expression_Closures", "expression closures", "", 1)}} do not bind <code>this</code> lexically.</li>
<li>Prior to <a href="/en-US/Firefox/Releases/39">Firefox 39</a>, a line terminator (<code>\n</code>) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES2015 specification and code like <code>() \n => {}</code> will now throw a {{jsxref("SyntaxError")}} in this and later versions.</li>
</ul>
<h2 id="Tìm_đọc">Tìm đọc</h2>
<ul>
<li><a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">"ES6 In Depth: Arrow functions" trên hacks.mozilla.org</a></li>
</ul>
|