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
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
|
---
title: 箭头函数
slug: Web/JavaScript/Reference/Functions/Arrow_functions
tags:
- ECMAScript 2015
- ES6 Arrow Function
- Functions
- Intermediate
- JavaScript
- Lambda
- Lambda Expression
- Reference
- ramda
translation_of: Web/JavaScript/Reference/Functions/Arrow_functions
---
<div>{{jsSidebar("Functions")}} </div>
<p><strong>箭头函数表达式</strong>的语法比<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/function">函数表达式</a>更简洁,并且没有自己的<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/this">this</a></code>,<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments">arguments</a></code>,<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/super">super</a></code>或<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/new.target">new.target</a></code>。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。</p>
{{EmbedInteractiveExample("pages/js/functions-arrow.html")}}
<h2 id="Syntax">语法</h2>
<blockquote>
<h3 id="基础语法">基础语法</h3>
<pre>(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
//相当于:(param1, param2, …, paramN) =>{ return expression; }
// 当只有一个参数时,<strong>圆括号</strong>是可选的:
(singleParam) => { statements }
singleParam => { statements }
// 没有参数的函数应该写成一对圆括号。
() => { statements }</pre>
</blockquote>
<h3 id="高级语法">高级语法</h3>
<blockquote>
<pre class="brush: js">//加括号的函数体返回对象字面量表达式:
params => ({foo: bar})
//支持<strong><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">剩余参数</a></strong>和<strong><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">默认参数</a></strong>
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }
//同样支持参数列表<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">解构</a>
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6</pre>
</blockquote>
<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'
];
elements.map(function(element) {
return element.length;
}); // 返回数组:[8, 6, 7, 9]
// 上面的普通函数可以改写成如下的箭头函数
elements.map((element) => {
return element.length;
}); // [8, 6, 7, 9]
// 当箭头函数只有一个参数时,可以省略参数的圆括号
elements.map(element => {
return element.length;
}); // [8, 6, 7, 9]
// 当箭头函数的函数体只有一个 `return` 语句时,可以省略 `return` 关键字和方法体的花括号
elements.map(element => element.length); // [8, 6, 7, 9]
// 在这个例子中,因为我们只需要 `length` 属性,所以可以使用参数解构
// 需要注意的是字符串 `"length"` 是我们想要获得的属性的名称,而 `lengthFooBArX` 则只是个变量名,
// 可以替换成任意合法的变量名
elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]
</pre>
<h3 id="没有单独的this">没有单独的<code>this</code></h3>
<p>在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值:</p>
<ul>
<li>如果该函数是一个构造函数,this指针指向一个新的对象</li>
<li>在严格模式下的函数调用下,this指向<code>undefined</code></li>
<li>如果该函数是一个对象的方法,则它的this指针指向这个对象</li>
<li>等等</li>
</ul>
<p><code>This</code>被证明是令人厌烦的面向对象风格的编程。</p>
<pre class="brush: js">function Person() {
// Person() 构造函数定义 `this`作为它自己的实例.
this.age = 0;
setInterval(function growUp() {
// 在非严格模式, growUp()函数定义 `this`作为全局对象,
// 与在 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 that = this;
that.age = 0;
setInterval(function growUp() {
// 回调引用的是`that`变量, 其值是预期的对象.
that.age++;
}, 1000);
}</pre>
<p>或者,可以创建<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">绑定函数</a>,以便将预先分配的<code>this</code>值传递到绑定的目标函数(上述示例中的<code>growUp()</code>函数)。</p>
<p>箭头函数不会创建自己的<code>this,它只会从自己的作用域链的上一层继承this</code>。因此,在下面的代码中,传递给<code>setInterval</code>的函数内的<code>this</code>与封闭函数中的<code>this</code>值相同:</p>
<pre class="brush: js">function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确地指向 p 实例
}, 1000);
}
var p = new Person();</pre>
<h4 id="与严格模式的关系">与严格模式的关系</h4>
<p>鉴于 <code>this</code> 是词法层面上的,<a href="/zh-CN/docs/Web/JavaScript/Reference/Strict_mode">严格模式</a>中与 <code>this</code> 相关的规则都将被忽略。</p>
<pre><code>var f = () => { 'use strict'; return this; };
f() === window; // 或者 global</code></pre>
<p>严格模式的其他规则依然不变.</p>
<h4 id="通过_call_或_apply_调用">通过 call 或 apply 调用</h4>
<p>由于 箭头函数没有自己的this指针,通过 <code>call()</code><em> 或</em> <code>apply()</code> 方法调用一个函数时,只能传递参数(不能绑定this---译者注),他们的第一个参数会被忽略。(这种现象对于bind方法同样成立---译者注)</p>
<pre class="brush: js">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)); // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2</pre>
<h3 id="不绑定arguments">不绑定<code>arguments</code></h3>
<p>箭头函数不绑定<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments">Arguments 对象</a>。因此,在本示例中,<code>arguments</code>只是引用了封闭作用域内的arguments:</p>
<pre class="brush: js">var arguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
var f = () => arguments[0] + n; // 隐式绑定 foo 函数的 arguments 对象. arguments[0] 是 n,即传给foo函数的第一个参数
return f();
}
foo(1); // 2
foo(2); // 4
foo(3); // 6
foo(3,2);//6
</pre>
<p>在大多数情况下,使用<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/Rest_parameters">剩余参数</a>是相较使用<code>arguments</code>对象的更好选择。</p>
<pre class="brush: js">function foo(arg) {
var f = (...args) => args[0];
return f(arg);
}
foo(1); // 1
function foo(arg1,arg2) {
var f = (...args) => args[1];
return f(arg1,arg2);
}
foo(1,2); //2
</pre>
<h3 id="使用箭头函数作为方法">使用箭头函数作为方法</h3>
<p>如上所述,箭头函数表达式对非方法函数是最合适的。让我们看看当我们试着把它们作为方法时发生了什么。</p>
<pre class="brush: js">'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b();
// undefined, Window{...}
obj.c();
// 10, Object {...}
</pre>
<p>箭头函数没有定义this绑定。另一个涉及{{jsxref("Object.defineProperty()")}}的示例:</p>
<pre class="brush: js">'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this);
return this.a+10;
// 代表全局对象 'Window', 因此 'this.a' 返回 'undefined'
}
});
obj.b; // undefined "undefined" Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
</pre>
<h3 id="使用_new_操作符">使用 <code>new</code> 操作符</h3>
<p>箭头函数不能用作构造器,和 <code>new</code>一起用会抛出错误。</p>
<pre class="brush: js">var Foo = () => {};
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 = () => {};
console.log(Foo.prototype); // undefined</pre>
<h3 id="使用_yield_关键字">使用 <code>yield</code> 关键字</h3>
<p> <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield">yield</a></code> 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作函数生成器。</p>
<h2 id="函数体">函数体</h2>
<p>箭头函数可以有一个“简写体”或常见的“块体”。</p>
<p>在一个简写体中,只需要一个表达式,并附加一个隐式的返回值。在块体中,必须使用明确的<code>return</code>语句。</p>
<pre class="brush: js">var func = x => x * x;
// 简写函数 省略return
var func = (x, y) => { return x + y; };
//常规编写 明确的返回值</pre>
<h2 id="返回对象字面量">返回对象字面量</h2>
<p>记住用<code>params => {object:literal}</code>这种简单的语法返回对象字面量是行不通的。</p>
<pre class="brush: js">var func = () => { foo: 1 };
// Calling func() returns undefined!
var func = () => { foo: function() {} };
// SyntaxError: function statement requires a name</pre>
<p>这是因为花括号(<code>{}</code> )里面的代码被解析为一系列语句(即 <code>foo</code> 被认为是一个标签,而非对象字面量的组成部分)。</p>
<p>所以,记得用圆括号把对象字面量包起来:</p>
<pre class="brush: js">var func = () => ({foo: 1});</pre>
<h2 id="换行">换行</h2>
<p>箭头函数在参数和箭头之间不能换行。</p>
<pre class="brush: js">var func = ()
=> 1;
// SyntaxError: expected expression, got '=>'</pre>
<p>但是,可以通过在 ‘=>’ 之后换行,或者用 ‘( )’、'{ }'来实现换行,如下:</p>
<pre class="brush: js">var func = (a, b, c) =>
1;
var func = (a, b, c) => (
1
);
var func = (a, b, c) => {
return 1
};
var func = (
a,
b,
c
) => 1;
// 不会有语法错误</pre>
<h2 id="解析顺序">解析顺序</h2>
<p>虽然箭头函数中的箭头不是运算符,但箭头函数具有与常规函数不同的特殊<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence">运算符优先级</a>解析规则。</p>
<pre class="brush: js">let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments
callback = callback || (() => {}); // ok</pre>
<h2 id="更多示例">更多示例</h2>
<pre class="brush: js">// 空的箭头函数返回 undefined
let empty = () => {};
(() => 'foobar')();
// Returns "foobar"
// (这是一个立即执行函数表达式,可参阅 'IIFE'术语表)
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]
// 更简明的promise链
promise.then(a => {
// ...
}).then(b => {
// ...
});
// 无参数箭头函数在视觉上容易分析
setTimeout( () => {
console.log('I happen sooner');
setTimeout( () => {
// deeper code
console.log('I happen later');
}, 1);
}, 1);</pre>
<h4 id="箭头函数也可以使用条件(三元)运算符:">箭头函数也可以使用条件(三元)运算符:</h4>
<pre class="brush: js">var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10
let max = (a, b) => a > b ? a : b;</pre>
<blockquote>
<p>箭头函数内定义的变量及其作用域</p>
</blockquote>
<pre class="brush: js">// 常规写法
var greeting = () => {let now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now); // ReferenceError: now is not defined 标准的let作用域
// 参数括号内定义的变量是局部变量(默认参数)
var greeting = (now=new Date()) => "Good" + (now.getHours() > 17 ? " evening." : " day.");
greeting(); //"Good day."
console.log(now); // ReferenceError: now is not defined
// 对比:函数体内{}不使用var定义的变量是全局变量
var greeting = () => {now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now); // Fri Dec 22 2017 10:01:00 GMT+0800 (中国标准时间)
// 对比:函数体内{} 用var定义的变量是局部变量
var greeting = () => {var now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now); // ReferenceError: now is not defined
</pre>
<blockquote>
<h4 id="箭头函数也可以使用闭包:">箭头函数也可以使用闭包:</h4>
</blockquote>
<pre class="brush: js">// 标准的闭包函数
function A(){
var i=0;
return function b(){
return (++i);
};
};
var v=A();
v(); //1
v(); //2
//箭头函数体的闭包( i=0 是默认参数)
var Add = (i=0) => {return (() => (++i) )};
var v = Add();
v(); //1
v(); //2
//因为仅有一个返回,return 及括号()也可以省略
var Add = (i=0)=> ()=> (++i);
</pre>
<blockquote>
<h4 id="箭头函数递归"> 箭头函数递归</h4>
</blockquote>
<pre class="brush: js">var fact = (x) => ( x==0 ? 1 : x*fact(x-1) );
fact(5); // 120</pre>
<p>规范</p>
<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('ES6', '#sec-arrow-function-definitions', 'Arrow Function Definitions')}}</td>
<td>{{Spec2('ES6')}}</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="Browser_Compatibility">浏览器兼容</h2>
<p>{{Compat("javascript.functions.arrow_functions")}}</p>
<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>
|