ES6之箭头函数

87 阅读4分钟
/**
1. 回顾
顾下箭头函数的基本语法。
ES6 增加了箭头函数:
let func = value => value;
相当于:
let func = function (value) {
  return value;
}
如果需要给函数传入多个参数:
let func = (value, num) => value * num;
如果函数的代码块需要多条语句:
let func = (value, num) => {
    return value * num
};
如果需要直接返回一个对象:
let func = (value, num) => ({total: value * num});

与变量解构结合:
let func = ({value, num}) => ({total: value * num})

// 使用
var result = func({
    value: 10,
    num: 10
})
console.log(result); // {total: 100}

很多时候,你可能想不到要这样用,所以再来举个例子,比如在 React 与 Immutable 的技术选型中,我们处理一个事件会这样做:
handleEvent = () => {
  this.setState({
    data: this.state.data.set("key", "value")
  })
}
其实就可以简化为:
handleEvent = () => {
  this.setState(({data}) =>({
    data: data.set("key", "value")
  }))
}

2. 比较
  1. 没有this
  箭头函数没有this 所以需要通过查找作用域链来确定this的值
  模拟一个实际开发中的例子:
  我们的需求是点击一个按钮,改变该按钮的背景色。
  为了方便开发,我们抽离一个 Button 组件,当需要使用的时候,直接:
  new Button('button')
  <button id="button">点击变色</button>
  JavaScript 代码如下:
  function Button(id) {
    this.element = document.querySelector('#' + id)
    this.bindEvent()
  }
  Button.prototype.bindEvent = function() {
    this.element.addEventListener('click', this.setBgColor, false)
  }
  Button.prototype.setBgColor = function(){
    this.element.style.background = '#1abc9c'
  }
  var button = new Button('button')
  看着好像没有问题,结果却是报错 Uncaught TypeError: Cannot read property 'style' of undefined
  因为当使用 addEventListener() 为一个元素注册事件的时候,事件函数里的 this 值是该元素的引用。
  如果我们在 setBgColor 中 console.log(this),this 指向的是按钮元素,那 this.element 就是 undefined,报错自然就理所当然了。
  也许你会问既然this都指向了这个按钮元素,我们直接膝盖setBgColor 不就可以解决问题了吗
  Button.prototype.setBgColor = function() {
    this.style.backgroundColor = '#1abc9c'
  };
  确实可以这样做,但是在实际的开发中,我们可能会在 setBgColor 中还调用其他的函数,比如写成这种:
  Button.prototype.setBgColor = function() {
    this.setElementColor()
    this.setOtherElementColor()
  }
  所以我们还是希望 setBgColor 中的 this 是指向实例对象的,这样就可以调用其他的函数
  利用 ES5,我们一般会这样做:
  Button.prototype.bindEvent = function() {
    this.element.addEventListener('click', this.setBgColor.bind(this), false)
  }
  为避免 addEventListener 的影响,使用 bind 强制绑定 setBgColor() 的 this 为实例对象
  使用 ES6,我们可以更好的解决这个问题:
  Button.prototype.bindEvent = function() {
    this.element.addEventListener('click, event => this.setBgColor(event), false)
  }
  由于箭头函数没有 this,所以会向外层查找 this 的值,即 bindEvent 中的 this,此时 this 指向实例对象,所以可以正确的调用 this.setBgColor 方法, 而 this.setBgColor 中的 this 也会正确指向实例对象。
  在这里再额外提一点,就是注意 bindEvent 和  setBgColor 在这里使用的是普通函数的形式,而非箭头函数,如果我们改成箭头函数,会导致函数里的 this 指向 window 对象 (非严格模式下)。
  最后,因为箭头函数没有 this,所以也不能用 call()、apply()、bind() 这些方法改变 this 的指向,可以看一个例子:
  var value = 1;
  var result = (() => this.value).bind({value: 2})();
  console.log(result); // 1

  2. 没有arguments
  箭头函数没有自己的arguments对象,这不一定是坏事,因为箭头函数可以访问外围函数的 arguments 对象:
  function constant() {
    return () => arguments[0]
  }
  var result = constant(1)
  console.log(result()) // 1
  那如果我们就是要访问箭头函数的参数呢?
  那你可以通过命名参数或者rest参数 的形式访问参数
  let nums = (...nums) => nums

  3. 不能通过new调用
  JavaScript 函数有两个内部方法:[[call]][[Construct]]
  当通过new调用函数时,执行[[Construct]] 方法,创建一个实例对象,然后再执行函数体将this绑定在实例上
  当直接调用时,执行[[Call]] 方法,直接执行函数图
  箭头函数并没有[[Construct]] 方法,不能被用做构造函数,如果用new方式调用,会报错
  var Foo = () => {}
  var foo = new Foo() // TypeError Foo is not a constructor

  4. 没有new target
  因为不能使用new调用所以也没有new.target值
  参考 https://es6.ruanyifeng.com/#docs/class#new-target-%E5%B1%9E%E6%80%A7

  5. 没有原型
  由于不能使用new调用箭头函数,所以也没有构建原型的需求,于是箭头函数没有prototype这个属性
  var Foo = () => {}
  console.log(Foo.prototype) // undefined

  6. 没有super
  连原型都没有,自然也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的,不过跟 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。

3. 总结
  最后,关于箭头函数,引用 MDN 的介绍就是:
  [An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors]
  箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数(non-method functions),并且它们不能用作构造函数。
  那么什么是 non-method functions 呢?
  我们先来看看 method 的定义:
  A method is a function which is a property of an object.
  对象属性中的函数就被称之为 method,那么 non-mehtod 就是指不被用作对象属性中的函数了,可是为什么说箭头函数更适合 non-method 呢?
  var obj= {
    i: 10,
    b: () => console.log(this.i, this),
    c: function() {
      console.log(this.i, this)
    }
  }
  obj.b()
  // undefined Window
  obj.c()
  // 10 Object{...}


4. 自执行函数
(function () {
  console.log(1)
})
(function(){
    console.log(1)
}())

这个会报错,但是什么原因请自己查找
(() => {
  console.log(1)
}()) 

 */