箭头函数表达式
是句法紧凑的替代对于常规
函数表达式
,尽管不会把自己绑定到
this
,
arguments
,
super
,或
new.target
关键词。箭头函数表达式不适合用作方法,且不可以用作构造函数。
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }
// The parameter list for a function with no parameters should be written with a pair of parentheses.
() => { statements }
// Parenthesize the body of a function to return an object literal expression:
params => ({foo: bar})
// Rest parameters and default parameters are supported
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }
// Destructuring within the parameter list is also supported
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
另请参阅 在 hacks.mozilla.org 中的 ES6 深入:箭头函数 .
Two factors influenced the introduction of arrow functions: the need for shorter functions and the behavior of the
this
关键词。
var elements = [ 'Hydrogen', 'Helium', 'Lithium', 'Beryllium' ]; // This statement returns the array: [8, 6, 7, 9] elements.map(function(element) { return element.length; }); // The regular function above can be written as the arrow function below elements.map((element) => { return element.length; }); // [8, 6, 7, 9] // When there is only one parameter, we can remove the surrounding parentheses elements.map(element => { return element.length; }); // [8, 6, 7, 9] // When the only statement in an arrow function is `return`, we can remove `return` and remove // the surrounding curly brackets elements.map(element => element.length); // [8, 6, 7, 9] // In this case, because we only need the length property, we can use destructuring parameter: // Notice that the `length` corresponds to the property we want to get whereas the // obviously non-special `lengthFooBArX` is just the name of a variable which can be changed // to any valid variable name you want elements.map(({ length: lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9] // This destructuring parameter assignment can also be written as seen below. However, note that in // this example we are not assigning `length` value to the made up property. Instead, the literal name // itself of the variable `length` is used as the property we want to retrieve from the object. elements.map(({ length }) => length); // [8, 6, 7, 9]
this
Before arrow functions, every new function defined its own
this
value based on how the function was called:
undefined
in
严格模式
function calls.
This proved to be less than ideal with an object-oriented style of programming.
function Person() {
// The Person() constructor defines `this` as an instance of itself.
this.age = 0;
setInterval(function growUp() {
// In non-strict mode, the growUp() function defines `this`
// as the global object (because it's where growUp() is executed.),
// which is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}
var p = new Person();
在 ECMAScript 3/5,
this
issue was fixable by assigning the value in
this
to a variable that could be closed over.
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// The callback refers to the `that` variable of which
// the value is the expected object.
that.age++;
}, 1000);
}
Alternatively, a
bound function
could be created so that a preassigned
this
value would be passed to the bound target function (the
growUp()
function in the example above).
An arrow function does not have its own
this
。
this
value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for
this
which is not present in the current scope, an arrow function ends up finding the
this
from its enclosing scope.
Thus, in the following code, the
this
within the function that is passed to
setInterval
has the same value as the
this
in the lexically enclosing function:
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the Person object
}, 1000);
}
var p = new Person();
Given that
this
comes from the surrounding lexical context,
严格模式
rules with regard to
this
被忽略。
var f = () => { 'use strict'; return this; };
f() === window; // or the global object
All other strict mode rules apply normally.
CORRECTION: START
NOTE: the previous statement seems false.
Strict mode should prevent creating global variables when assigning to an undeclared identifier in a function.
This code sample using Chrome 81 demonstrates that arrow functions allow the creation of global variables in such situations (both for a concise body and for a normal function body):
> f1 = x => { y = x; console.log(`x: ${x}, y: ${y}`); return x + 1; }
x => { y = x; console.log(`x: ${x}, y: ${y}`); return x + 1; }
> y
VM51587:1 Uncaught ReferenceError: y is not defined
at <anonymous>:1:1
(anonymous) @ VM51587:1
> f1(3)
VM51533:1 x: 3, y: 3
4
> y
3
> f2 = x => { 'use strict'; z = x; console.log(`x: ${x}, z: ${z}`); return x + 1; }
x => { 'use strict'; z = x; console.log(`x: ${x}, z: ${z}`); return x + 1; }
> z
VM51757:1 Uncaught ReferenceError: z is not defined
at <anonymous>:1:1
(anonymous) @ VM51757:1
> f2(4)
VM51712:1 Uncaught ReferenceError: z is not defined
at f2 (<anonymous>:1:29)
at <anonymous>:1:1
f2 @ VM51712:1
(anonymous) @ VM51800:1
> f3 = x => (z1 = x + 1)
x => (z1 = x + 1)
> z1
VM51891:1 Uncaught ReferenceError: z1 is not defined
at <anonymous>:1:1
(anonymous) @ VM51891:1
> f3(10)
11
> z1
11
f2 illustrates that when explicitly setting the arrow function to apply strict mode, it does throw an error when attempting to assign an undeclared variable.
https://www.ecma-international.org/ecma-262/10.0/index.html#sec-strict-mode-code
https://www.ecma-international.org/ecma-262/10.0/index.html#sec-arrow-function-definitions-runtime-semantics-evaluation
CORRECTION: END
Since arrow functions do not have their own
this
, the methods
call()
and
apply()
can only pass in parameters. Any
this
argument is ignored.
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)); // This would log 2
console.log(adder.addThruCall(1)); // This would log 2 still
arguments
Arrow functions do not have their own
arguments
object
. Thus, in this example,
arguments
is simply a reference to the arguments of the enclosing scope:
var arguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
return f();
}
foo(3); // 6
In most cases, using
rest parameters
is a good alternative to using an
arguments
对象。
function foo(n) {
var f = (...args) => args[0] + n;
return f(10);
}
foo(1); // 11
As stated previously, arrow function expressions are best suited for non-method functions. Let's see what happens when we try to use them as methods:
'use strict';
var obj = { // does not create a new scope
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log(this.i, this);
}
}
obj.b(); // prints undefined, Window {...} (or the global object)
obj.c(); // prints 10, Object {...}
Arrow functions do not have their own
this
. Another example involving
Object.defineProperty()
:
'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, 'b', {
get: () => {
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'
}
});
new
operator
Arrow functions cannot be used as constructors and will throw an error when used with
new
.
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
prototype
property
箭头函数没有
prototype
特性。
var Foo = () => {};
console.log(Foo.prototype); // undefined
yield
keyword
yield
keyword may not be used in an arrow function's body (except when permitted within functions further nested within it). As a consequence, arrow functions cannot be used as generators.
Arrow functions can have either a "concise body" or the usual "block body".
In a concise body, only an expression is specified, which becomes the implicit return value. In a block body, you must use an explicit
return
语句。
var func = x => x * x;
// concise body syntax, implied "return"
var func = (x, y) => { return x + y; };
// with block body, explicit "return" needed
Keep in mind that returning object literals using the concise body syntax
params => {object:literal}
will not work as expected.
var func = () => { foo: 1 };
// Calling func() returns undefined!
var func = () => { foo: function() {} };
// SyntaxError: function statement requires a name
This is because the code inside braces ({}) is parsed as a sequence of statements (i.e.
foo
is treated like a label, not a key in an object literal).
You must wrap the object literal in parentheses:
var func = () => ({ foo: 1 });
An arrow function cannot contain a line break between its parameters and its arrow.
var func = (a, b, c) => 1; // SyntaxError: expected expression, got '=>'
However, this can be amended by putting the line break after the arrow or using parentheses/braces as seen below to ensure that the code stays pretty and fluffy. You can also put line breaks between arguments.
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;
// no SyntaxError thrown
Although the arrow in an arrow function is not an operator, arrow functions have special parsing rules that interact differently with 运算符优先级 compared to regular functions.
let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments
callback = callback || (() => {}); // ok
// An empty arrow function returns undefined
let empty = () => {};
(() => 'foobar')();
// Returns "foobar"
// (this is an Immediately Invoked Function Expression)
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);
| 规范 |
|---|
|
ECMAScript (ECMA-262)
在该规范中的 Arrow Function Definitions 定义。 |
| Desktop | Mobile | Server | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 箭头函数 | Chrome 45 | Edge 12 |
Firefox
22
|
IE No | Opera 32 | Safari 10 | WebView Android 45 | Chrome Android 45 |
Firefox Android
22
|
Opera Android 32 | Safari iOS 10 | Samsung Internet Android 5.0 | nodejs Yes |
| Trailing comma in parameters | Chrome 58 | Edge 12 | Firefox 52 | IE No | Opera 45 | Safari 10 | WebView Android 58 | Chrome Android 58 | Firefox Android 52 | Opera Android 43 | Safari iOS 10 | Samsung Internet Android 7.0 | nodejs Yes |
完整支持
不支持
见实现注意事项。