试一试
const materials = ["Hydrogen", "Helium", "Lithium", "Beryllium"];
console.log(materials.map((material) => material.length));
// Expected output: Array [8, 6, 7, 9]
语法
() => expression
param => expression
(param) => expression
(param1, paramN) => expression
() => {
statements
}
param => {
statements
}
(param1, paramN) => {
statements
}
(a, b, ...r) => expression
(a = 400, b = 20, c) => expression
([a, b] = [10, 20]) => expression
({ a, b } = { a: 10, b: 20 }) => expression
通过在表达式前加上async关键字,箭头函数可以是async函数。
async param => expression
async (param1, param2, ...paramN) => {
statements
}
描述
让我们一步步地将一个传统的匿名函数分解为最简单的箭头函数。每一步都是一个有效的箭头函数。
注意:传统函数表达式和箭头函数除了语法之外还有更多差异。我们将在接下来的几个小节中更详细地介绍它们的行为差异。
// Traditional anonymous function
(function (a) {
return a + 100;
});
// 1. Remove the word "function" and place arrow between the argument and opening body brace
(a) => {
return a + 100;
};
// 2. Remove the body braces and word "return" — the return is implied.
(a) => a + 100;
// 3. Remove the parameter parentheses
a => a + 100;
在上面的例子中,参数周围的括号和函数体周围的大括号都可以省略。然而,它们只能在某些情况下省略。
只有当函数只有一个简单参数时,才能省略括号。如果它有多个参数、没有参数,或者有默认参数、解构参数或剩余参数,则参数列表周围的括号是必需的。
// Traditional anonymous function
(function (a, b) {
return a + b + 100;
});
// Arrow function
(a, b) => a + b + 100;
const a = 4;
const b = 2;
// Traditional anonymous function (no parameters)
(function () {
return a + b + 100;
});
// Arrow function (no parameters)
() => a + b + 100;
只有当函数直接返回一个表达式时,才能省略大括号。如果函数体有语句,则大括号是必需的——return关键字也是必需的。箭头函数无法猜测你想要返回什么或何时返回。
// Traditional anonymous function
(function (a, b) {
const chuck = 42;
return a + b + chuck;
});
// Arrow function
(a, b) => {
const chuck = 42;
return a + b + chuck;
};
箭头函数本身没有关联的名称。如果箭头函数需要调用自身,请使用命名函数表达式。你也可以将箭头函数赋值给一个变量,这样你就可以通过该变量引用它。
// Traditional Function
function bob(a) {
return a + 100;
}
// Arrow Function
const bob2 = (a) => a + 100;
函数体
箭头函数可以有表达式体或通常的块体。
在表达式体中,只指定一个表达式,它成为隐式返回值。在块体中,你必须使用显式的return语句。
const func = (x) => x * x;
// expression body syntax, implied "return"
const func2 = (x, y) => {
return x + y;
};
// with block body, explicit "return" needed
使用表达式体语法(params) => { object: literal }返回对象字面量不起作用,因为这并不是预期行为。
const func = () => { foo: 1 };
// Calling func() returns undefined!
const func2 = () => { foo: function () {} };
// SyntaxError: function statement requires a name
const func3 = () => { foo() {} };
// SyntaxError: Unexpected token '{'
这是因为 JavaScript 只有在箭头后面的 token 不是左大括号时,才将箭头函数视为具有表达式体,因此大括号({})内的代码被解析为一系列语句,其中foo是一个标签,而不是对象字面量中的键。
为了解决这个问题,将对象字面量用括号括起来。
const func = () => ({ foo: 1 });
不能作为方法使用
箭头函数表达式只应用于非方法函数,因为它们没有自己的this。让我们看看当我们尝试将它们用作方法时会发生什么。
"use strict";
const obj = {
i: 10,
b: () => console.log(this.i, this),
c() {
console.log(this.i, this);
},
};
obj.b(); // logs undefined, Window { /* … */ } (or the global object)
obj.c(); // logs 10, Object { /* … */ }
另一个涉及Object.defineProperty()的例子。
"use strict";
const 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'
},
});
因为类的函数体有一个this上下文,所以作为类字段的箭头函数会关闭该类的this上下文,并且箭头函数函数体内的this将正确指向实例(或对于静态字段,指向类本身)。然而,由于它是一个闭包,而不是函数自身的绑定,this的值不会根据执行上下文而改变。
class C {
a = 1;
autoBoundMethod = () => {
console.log(this.a);
};
}
const c = new C();
c.autoBoundMethod(); // 1
const { autoBoundMethod } = c;
autoBoundMethod(); // 1
// If it were a normal method, it should be undefined in this case
箭头函数属性通常被称为“自动绑定方法”,因为与普通方法等价的是:
class C {
a = 1;
constructor() {
this.method = this.method.bind(this);
}
method() {
console.log(this.a);
}
}
注意:类字段定义在实例上,而不是原型上,因此每次创建实例都会创建一个新的函数引用并分配一个新的闭包,这可能导致比普通非绑定方法更多的内存使用。
由于类似的原因,当在箭头函数上调用call()、apply()和bind()方法时,它们并不那么有用,因为箭头函数会根据定义它的作用域来建立this,并且this的值不会根据函数的调用方式而改变。
没有arguments绑定
箭头函数没有自己的arguments对象。因此,在这个例子中,arguments是对封闭作用域的参数的引用。
function foo(n) {
const f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
return f();
}
foo(3); // 3 + 3 = 6
在大多数情况下,使用剩余参数是使用arguments对象的一个很好的替代方案。
function foo(n) {
const f = (...args) => args[0] + n;
return f(10);
}
foo(1); // 11
不能作为构造函数使用
箭头函数不能作为构造函数使用,并且在使用new调用时会抛出错误。它们也没有prototype属性。
const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor
console.log("prototype" in Foo); // false
不能作为生成器使用
在箭头函数的函数体中不能使用yield关键字(除非在箭头函数中进一步嵌套的生成器函数中使用)。因此,箭头函数不能用作生成器。
箭头前的换行符
箭头函数不能在其参数和箭头之间包含换行符。
const func = (a, b, c)
=> 1;
// SyntaxError: Unexpected token '=>'
为了格式化,你可以在箭头后面放置换行符,或者在函数体周围使用括号/大括号,如下所示。你也可以在参数之间放置换行符。
const func = (a, b, c) =>
1;
const func2 = (a, b, c) => (
1
);
const func3 = (a, b, c) => {
return 1;
};
const func4 = (
a,
b,
c,
) => 1;
箭头的优先级
虽然箭头函数中的箭头不是运算符,但箭头函数有特殊的解析规则,与常规函数相比,它与运算符优先级的交互方式不同。
let callback;
callback = callback || () => {};
// SyntaxError: invalid arrow-function arguments
因为=>的优先级低于大多数运算符,所以需要括号来避免callback || ()被解析为箭头函数的参数列表。
callback = callback || (() => {});
示例
使用箭头函数
// An empty arrow function returns undefined
const empty = () => {};
(() => "foobar")();
// Returns "foobar"
// (this is an Immediately Invoked Function Expression)
const simple = (a) => (a > 15 ? 15 : a);
simple(16); // 15
simple(10); // 10
const max = (a, b) => (a > b ? a : b);
// Easy array filtering, mapping, etc.
const arr = [5, 6, 13, 0, 1, 18, 23];
const sum = arr.reduce((a, b) => a + b);
// 66
const even = arr.filter((v) => v % 2 === 0);
// [6, 0, 18]
const double = arr.map((v) => v * 2);
// [10, 12, 26, 0, 2, 36, 46]
// More concise promise chains
promise
.then((a) => {
// …
})
.then((b) => {
// …
});
// Arrow functions without parameters
setTimeout(() => {
console.log("I happen sooner");
setTimeout(() => {
// deeper code
console.log("I happen later");
}, 1);
}, 1);
使用 call、bind 和 apply
在传统函数中,call()、apply()和bind()方法按预期工作,因为我们为每个方法建立了作用域。
const obj = {
num: 100,
};
// Setting "num" on globalThis to show how it is NOT used.
globalThis.num = 42;
// A traditional function to operate on "this"
function add(a, b, c) {
return this.num + a + b + c;
}
console.log(add.call(obj, 1, 2, 3)); // 106
console.log(add.apply(obj, [1, 2, 3])); // 106
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 106
对于箭头函数,由于我们的add函数基本上是在globalThis(全局)作用域中创建的,它将假定this是globalThis。
const obj = {
num: 100,
};
// Setting "num" on globalThis to show how it gets picked up.
globalThis.num = 42;
// Arrow function
const add = (a, b, c) => this.num + a + b + c;
console.log(add.call(obj, 1, 2, 3)); // 48
console.log(add.apply(obj, [1, 2, 3])); // 48
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3)); // 48
也许使用箭头函数的最大好处是与setTimeout()和EventTarget.prototype.addEventListener()等方法一起使用,这些方法通常需要某种闭包、call()、apply()或bind()来确保函数在正确的范围中执行。
使用传统函数表达式,这样的代码无法按预期工作:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(function () {
// the function executes on the window scope
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // logs "NaN", because the property "count" is not in the window scope.
使用箭头函数,this作用域更容易保留:
const obj = {
count: 10,
doSomethingLater() {
// The method syntax binds "this" to the "obj" context.
setTimeout(() => {
// Since the arrow function doesn't have its own binding and
// setTimeout (as a function call) doesn't create a binding
// itself, the "obj" context of the outer method is used.
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // logs 11
规范
| 规范 |
|---|
| ECMAScript® 2026 语言规范 # sec-arrow-function-definitions |
浏览器兼容性
加载中…
另见
- 函数指南
- 函数
functionfunction表达式- ES6 深入:箭头函数,来自 hacks.mozilla.org (2015)