/ JavaScript

ES6 箭头函数语法及词法作用域

ES6 引入了「箭头函数」(arrow function),以简化输入、节约时间。

但是,箭头函数不是用来取代 function 关键字的。如果掌握了 JavaScript 词法作用域、this 关键字、.call()/.apply()/.bind() 等原型方法,就比较容易理解箭头函数了。

语法

先看一下来自 MDN 的例子:

// example 1
([param] [, param]) => {
  statements
}

// example 2
param => expression

上面的代码转换为 ES5 后等同于:

// example 1
function ([param] [, param]) {
  statements
}

// example 2
function (param) {
  return expression
}

example 1 中,ES6 省略了 function 关键字,并在参数后面添加了 =>example 2 中,ES6 缩减为一行,编写返回值的单行函数表达式时非常方便。

注意:箭头函数是匿名的

箭头函数始终是匿名的,使用箭头函数创建命名函数时需要将其赋值给变量。

// ES5
function doSomething() {
  //...
}

// ES6
var doSomething = () => {
  //...
}

语法:单行表达式

比如 Array.prototype.map 的使用:

// ES5
var numbers = [1,2,3,4,5];
var timesTwo = numbers.map(function (number) {
  return number * 2;
});
console.log(timesTwo); // [2, 4, 6, 8, 10]

// ES6
var numbers = [1,2,3,4,5];
var timesTwo = numbers.map((number) => number * 2);
console.log(timesTwo); // [2, 4, 6, 8, 10]

语法:单参数函数

箭头函数提供了一个语法糖,允许只有一个参数时移除圆括号。

以上面的代码为例:

numbers.map((number) => number * 2);

可以省略 (number) 的括号:

numbers.map(number => number * 2);

这样简洁了一些,但建议始终保留括号,以方便维护(比如以后需要增加参数时)。

// we still rock with ES6
numbers.map((number) => number * 2);

箭头函数的词法作用域功能

ES5 中,一般使用 .bind() 之类的方法传递 this,以改变函数的执行上下文,比如具有不同作用域的回调函数里。

function FooCtrl (FooService) {
  this.foo = 'Hello';
  FooService.doSomething(function (response) {
    this.foo = response;
  });
}

this.foo = response; 不会像预期的那样工作,因为 this 已经改变。使用 .bind(this) 可以达到预期的效果:

function FooCtrl (FooService) {
  this.foo = 'Hello';
  FooService.doSomething(function (response) {
    this.foo = response;
  }.bind(this));
}

或者使用一个局部的变量来保存 this,尤其是在有多个嵌套上下文的时候(.bind() 执行起来非常慢)。使用 _this 来保存 this 引用:

function FooCtrl (FooService) {
  var _this = this;
  _this.foo = 'Hello';
  FooService.doSomething(function (response) {
    _this.foo = response;
  });
}

现在有了更好的选择:箭头函数会自动绑定 this 为定义时所在的上下文,而不是执行时候的上下文。

function FooCtrl (FooService) {
  this.foo = 'Hello';
  FooService.doSomething((response) => { // woo, pretty
    this.foo = response;
  });
}

再简化一下:

function FooCtrl (FooService) {
  this.foo = 'Hello';
  FooService.doSomething((response) => this.foo = response);
}

via ES6 arrow functions, syntax and lexical scoping