什么是ES6箭头功能,它们如何工作?

时间:2016-05-19 20:58:00

标签: javascript ecmascript-6 arrow-functions

我很好奇ES6箭头功能(胖箭头功能)。它们只是源自CoffeeScript的语法糖,还是对它们有更多的东西而不是眼睛?

1 个答案:

答案 0 :(得分:2)

ES6箭头功能深入

ES6最漂亮的功能之一,如果举办这样的比赛,它很容易赢得选美比赛。许多人不知道的是,箭头函数不仅仅是我们可以使用的一种语法糖,而不是常规的回调。 我想向参加我的培训/研讨会的人员解释一下,箭头函数是this - 少,arguments - 少,new.target - 少和super - 少。 现在让我们通过更短的语法,深入了解箭头函数的细节。

词汇绑定

以前,常规函数将this值设置为全局对象(如果它们用作回调),设置为新对象,以防它们使用new运算符调用,或者在此情况下像jQuery这样的库,它们将被设置为在事件处理程序的情况下触发事件的对象,或者$.each迭代中的当前元素。即使对于有经验的开发人员来说,这种情况也很困惑。 假设您有一段代码如下所示。

var obj = {
  nameValue: 'default',
  initializeHandlers: function() {
    var nameInput = document.querySelector('#name');

    nameInput.addEventListener('blur', function(event) {
      this.nameValue = event.target.value;
    });
  }
};

obj.initializeHandlers();

问题是this事件处理程序中的blur设置为全局对象而不是obj。在严格模式下‘use strict’; - 您可能会因为this设置为undefined而违反申请。为了解决这个问题,我们有两个选择:

  • 使用Function.prototype.bind
  • 将事件处理程序转换为绑定到外部作用域的函数
  • var self = this;函数中使用脏initializeHandlers表达式(我将其视为黑客)

这两个选项如下所示。

[...]
initializeHandlers: function() {
  var nameInput = document.querySelector('#name');
  // more elegant but we can do better
  var blurHandler = function(event) {
    this.nameValue = event.target.value;
  }.bind(this)

  nameInput.addEventListener('blur', blurHandler);
}
[...]

[...]
initializeHandlers: function() {
  var nameInput = document.querySelector('#name');
  // ugly and error-prone
  var self = this;

  nameInput.addEventListener('blur', function(event) {
    self.nameValue = event.target.value;
  });
}
[...]

另一方面,箭头函数没有内部上下文。它们从外部范围继承其上下文。我们来看看箭头函数如何解决这个问题。

const obj = {
  nameValue: 'default',
  initializeHandlers: function() {
    const nameInput = document.querySelector('#name');

    nameInput.addEventListener('blur', (event) => {
      // this references obj instead of the global object
      this.nameValue = event.target.value;
    });
  }
};

在我们的新实现中,this是对obj对象的硬引用,不会因嵌套而丢失。

词汇论证

您是否尝试过访问箭头功能中的arguments对象?我有,而且我浪费了3个小时试图弄清楚为什么我得到外部函数的参数而不是箭头函数的参数。 值得庆幸的是,MDN存在,并且作为良好的实践要求,你最后检查文档,当你坐在角落里,膝盖塞到你的胸口,摇摆并重复自己:“我应该是一个木匠!” 除此之外,箭头函数不会公开arguments对象。如果您尝试访问它,您将获得周围函数的参数。在我们的例子中,鉴于外部函数也是一个箭头函数,并且我们在链中没有更多函数,我们将获得ReferenceError

const variadicAdder = (x) => {

  return () => {
    let args = Array.prototype.slice.call(arguments, 0);
    return args.reduce((accumulator, current) => {
      return accumulator + current;
    }, x);
  }
}

const variadicAdderOf5 = variadicAdder(5);

console.log(variadicAdderOf5(10, 11, 12));
// ReferenceError: arguments is not defined

这里没有修复,因为没有任何破坏。我们可以做的是从variadicAdder()返回一个普通函数,而不是箭头。 这将使我们有机会无问题地访问arguments对象。更新的代码将如下所示,唯一的区别 它实际上会工作而不会抛出错误。

const variadicAdder = (x) => {

  return function() {
    let args = Array.prototype.slice.call(arguments, 0);
    return args.reduce((accumulator, current) => {
      return accumulator + current;
    }, x);
  }
}

const variadicAdderOf5 = variadicAdder(5);

console.log(variadicAdderOf5(10, 11, 12));
// 38

要了解有关Array.prototype.reduce的更多信息,请转到Mozilla Developer Network

其他特征

正如我在本文的介绍部分中提到的,除了上下文和参数之外,箭头函数还有其他几个特性。 我想提到的第一件事是你无法使用new运算符和箭头函数。直接暗示,箭头函数也没有super()。像下面这样的片段只会抛出一个TypeError

const Person = (name) => {
  this.name = name;
};

let p = new Person('John');
// TypeError: Person is not a constructor

第三个特征也是无法使用new运算符的直接含义,即箭头函数没有new.target这一事实。简而言之,new.target允许您检测函数是否已被调用为构造函数。 箭头函数从其周围范围继承new.target。如果外部作用域是一个函数,并且它被称为构造函数(例如new Person('Adrian');),那么new.target将指向外部函数。 Mozilla开发者网络提供了new.target的详细说明,我建议您查看。

此文章也发布在我的博客上:/es6-arrow-functions-in-depth/