一个箭头函数表达式(也被成为大箭头函数)是普通函数表达式的简写,它通过词法绑定来指定 this 指针的值(不会绑定自己的this,arguments,super,或是 new.target). 箭头函数总是匿名的.
语法
基础语法
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// 相当于: => { return expression; }
// 当只有一个参数的时候,’()’是可选的:
(singleParam) => { statements }
singleParam => { statements }
// 没有参数的函数必须要使用’()’
() => { statements }
高级语法
// 如果需要返回一个字面量对象,需要用’()’包括它.
params => ({foo: bar})
// 支持 Rest parameters 以及 default parameters
(param1, param2, …rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements }
// 参数列表里还支持使用 解构赋值 destructuring
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
更多语法例子查看: 这里
描述
也可以看这篇: “ES6 In Depth: Arrow functions” on hacks.mozilla.org
箭头函数引入了两个关键的影响因素: 简短函数和词法指针(this
).
简短函数
在一些函数模型下,简短函数很受欢迎.比较以下内容:
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
var a2 = a.map(function(s){ return s.length });
var a3 = a.map( s => s.length );
词法绑定指针(this
)
在箭头函数出现之前,每个函数的实例都有自己的指针(如果是一个构造函数,它就是实例化后的对象,如果是在严格模式下,直接调用函数,它就是undefined,如果是作为对象的方法被调用,它就是对象的上下文…等等…),这在面向对象编程里,是一个很麻烦的东西.
function Person() {
// Person() 构造函数里的 `this` 指向它自己的实例对象.
this.age = 0;
setInterval(function growUp() {
// 在非严格模式下, growUp()函数定义 this 指针指向全局对象,而不是Person()构造函数里的this.
this.age++;
}, 1000);
}
var p = new Person();
在 ECMAScript 3/5 里, 通过把this赋值给一个新的封闭的变量,可以解决这个问题.
function Person() {
var self = this; // 有些人习惯用 that,而不是 self.
// 习惯了用哪个就一直用下去.
self.age = 0;
setInterval(function growUp() {
// 回调里的self的值就是你期望的那个.
self.age++;
}, 1000);
}
另外,也可以创建一个 绑定了this的函数 bound function, 这样,正确的 this
值就会被传递到 growUp()
函数里.
箭头函数会捕捉到当前闭包环境下的 this
值,所以下面的代码会按照期望的那样运行:
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确的指向了person对象
}, 1000);
}
var p = new Person();
和严格模式的关系
假设 this
是词法绑定的,严格模式下关于 this
的相关规则会被忽视.
var f = () => {'use strict'; return this};
f() === window; // 或者是全局对象
其余严格模式规则都正常执行.
通过 call
或者 apply
调用
由于 this
已经被词法绑定了,通过 call()
和 apply()
调用只能起到传递参数的作用,不能改变 this
指针的指向:
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
词法绑定 arguments
箭头函数不会把参数对象 arguments
暴露给代码,所以 arguments.length
, arguments[0]
, arguments[1]
等得到的值和 this
一样,是词法绑定的,也就是说,它指向的是调用函数的闭包作用域下的 arguments
变量.
var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
var f = (i) => arguments[0]+i; // foo函数里隐式的绑定了arguments
return f(2);
}
foo(1); // 3
箭头函数没有自己的 arguments
对象,但是在大多数情况下,使用 rest parameters 是一个很好的选择:
function foo() {
var f = (...args) => args[0];
return f(2);
}
foo(1); // 2
使用 yield
关键词
yield关键词不能被用在箭头函数的函数体里.(除了用在函数里嵌套的函数里)所以,箭头函数不能被作为一个生成器.
函数体
箭头函数既可以使用 “简约格式” 也可以使用普通的 “代码块” 作为函数体
使用代码块格式不会自动返回一个值.你需要明确地使用 return
语句.
var func = x => x * x; // 简洁语法, 隐式调用 "return"
var func = (x, y) => { return x + y; }; // 通过代码块, 需要显式地使用 "return"
返回字面量对象
记住,使用简洁语法来返回字面量对象 params => {object:literal}
不会像你预期的那样执行:
var func = () => { foo: 1 }; // 调用 func() 返回 undefined!
var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name
这是因为花括号里内容会被当做语句顺序解析.(比如, foo
会被看做一个标签,而不是字面量对象的键)
记得,把字面量对象用’()’包起来:
var func = () => ({ foo: 1 });
一些栗子
// empty 箭头函数返回 undefined
let empty = () => {};
(() => "foobar")() // 返回 "foobar"
var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10
let max = (a, b) => a > b ? a : b;
// 简单的实现数组的过滤,迭代, ...
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 => {
// ...
});