Arrow Functions (箭头函数) 详解

一个箭头函数表达式(也被成为大箭头函数)是普通函数表达式的简写,它通过词法绑定来指定 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",
  "Beryl­lium"
];

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 => {
   // ...
});

原文地址