JavaScript箭头功能:为什么需要它们,如何处理它们,何时使用它们以及何时不使用它们

现代JavaScript最著名的创新之一就是箭头功能的出现,有时也称为“胖”箭头功能。 声明此类功能时,它们使用特殊的字符组合- =>

箭头功能比传统功能有两个主要优点。 第一个是非常方便且紧凑的语法。 第二个问题是,与普通功能相比,使用箭头功能处理this方法看起来更加直观。

图片

有时,这些优点和其他优点导致以下事实:与其他声明函数的方式相比,无条件地赋予了箭头语法。 例如,Airbnb流行的eslint配置强制每当创建匿名函数时,此类函数就会像箭头一样。

但是,与编程中使用的其他概念和机制一样,箭头功能也各有利弊。 它们的使用会导致负面的副作用。 为了正确使用箭头功能,您需要了解与它们相关的可能问题。

我们今天发布的翻译材料将集中于箭头功能的工作方式。 在这里,我们将考虑使用它们可以改善代码的情况以及不应该使用它们的情况。

在JavaScript中具有箭头功能


JavaScript中的箭头函数类似于Python中的lambda函数和Ruby中的

这些是具有特殊语法的匿名函数,它们带有固定数量的参数,并在包含它们的作用域的上下文中起作用,即在声明它们的函数或其他代码的上下文中起作用。

让我们详细讨论一下。

▍语法箭头功能


箭头功能是根据单个方案构建的,而在特殊情况下,可以简化功能的结构。 箭头函数的基本结构如下所示:

 (argument1, argument2, ... argumentN) => { //   } 

函数参数的列表在括号中,后跟由=>字符组成的箭头,然后以大括号形式出现在函数的主体中。

这与普通函数的工作原理非常相似,主要区别在于此处省略了function关键字,并在参数列表后添加了一个箭头。

但是,在某些情况下,可以使用更紧凑的结构声明简单的箭头函数。

如果函数的主体由单个表达式表示,请考虑使用的语法。 它使您无需使用大括号括住函数的主体,并且无需显式返回对表达式求值的结果,因为此结果将自动返回。 例如,它可能看起来像这样:

 const add = (a, b) => a + b; 

这是该函数的缩写符号的另一种变体,当该函数仅具有一个参数时使用。

 const getFirst = array => array[0]; 

如您所见,此处省略了参数列表的括号。 此外,在此示例中由单个命令表示的函数主体也编写为不带方括号的形式。 稍后我们将更多地讨论这种设计的好处。

objects返回对象和短记录箭头功能


使用箭头函数时,还会使用一些更复杂的语法构造,这对于您很有用。

例如,尝试使用单行表达式从对象文字函数返回。 鉴于我们对箭头函数的了解,似乎函数声明看起来像这样:

 (name, description) => {name: name, description: description}; 

这段代码的问题在于其含糊不清。 即,我们想要用来描述对象文字的花括号看起来像是我们试图将函数主体包含在其中。

为了向系统表明我们的意思是对象文字,我们需要将其括在括号中:

 (name, description) => ({name: name, description: description}); 

▍箭头函数及其执行上下文


与其他函数不同,箭头函数没有自己的执行上下文

实际上,这意味着它们从父函数继承了thisarguments实体。

例如,比较以下代码中提供的两个功能。 其中之一是普通的,第二是箭头。

 const test = { name: 'test object', createAnonFunction: function() {   return function() {     console.log(this.name);     console.log(arguments);   }; }, createArrowFunction: function() {   return () => {     console.log(this.name);     console.log(arguments);   }; } }; 

有两种方法的test对象。 它们每个都是创建并返回匿名函数的函数。 这些方法之间的区别仅在于,在第一种方法中,使用了传统的函数表达式,而在第二种方法中,即使用了箭头函数。

如果我们在控制台中尝试此代码,并将相同的参数传递给对象的方法,则尽管方法看起来非常相似,但我们将获得不同的结果:

 > const anon = test.createAnonFunction('hello', 'world'); > const arrow = test.createArrowFunction('hello', 'world'); > anon(); undefined {} > arrow(); test object { '0': 'hello', '1': 'world' } 

匿名函数具有其自己的上下文,因此在调用匿名函数时,当您调用test.name ,将不test.name对象的name属性值,并且在调用arguments ,将不显示用于创建和返回所调查函数的函数的参数列表。

对于箭头函数,事实证明其上下文与创建它的函数的上下文一致,这使它可以访问此函数传递的参数列表以及该函数作为方法的对象的name属性。

箭头功能可改善代码的情况


▍处理值列表


传统的lambda函数以及箭头函数在JavaScript中出现后,通常用于将特定函数应用于特定列表的每个元素的情况。

例如,如果存在需要使用map arrays方法转换的值数组,则箭头函数非常适合描述这种转换:

 const words = ['hello', 'WORLD', 'Whatever']; const downcasedWords = words.map(word => word.toLowerCase()); 

这是箭头功能类似用法的一个极其常见的示例,其中包括使用对象的属性:

 const names = objects.map(object => object.name); 

类似地,如果不是使用传统的for循环,而是使用基于迭代器的现代forEach循环,那么该箭头函数将使用父实体的循环,从而使使用直观:

 this.examples.forEach(example => { this.runExample(example); }); 

▍承诺和承诺链


箭头功能允许您编写更简洁,更易理解的代码的另一种情况是异步软件结构。

因此, promise大大简化了异步代码的工作。 同时,即使您更喜欢使用async / await 构造,您也不能不理解promise ,因为promise是基于它们的。

但是,在使用Promise时,您需要声明在异步代码完成或对特定API的异步调用完成之后调用的函数。

这是使用箭头函数的理想位置,尤其是在结果函数具有特定状态的情况下,它指向对象中的某物。 例如,它可能看起来像这样:

 this.doSomethingAsync().then((result) => { this.storeResult(result); }); 

▍对象转换


箭头函数的另一个常见用例是封装对象转换。

例如,在Vue.js中,存在一种通用模式,可使用mapState Vuex存储片段直接包括在Vue组件中。

该操作包括一组“转换器”的声明,这些转换器从初始完全状态中准确选择特定组件所需的内容。

这些简单的转换是使用箭头功能的理想场所。 例如:

 export default { computed: {   ...mapState({     results: state => state.results,     users: state => state.users,   }); } } 

不应使用箭头功能的情况


▍对象方法


在许多情况下,使用箭头功能不是一个好主意。 箭头功能如果使用得很少,不仅不会帮助程序员,还会成为问题的根源。

第一种情况是将箭头函数用作对象方法。 执行上下文和this (特定于传统功能)在这里很重要。

一次,使用类属性和箭头函数的组合来创建具有“自动绑定”的方法是很流行的,也就是说,那些可以被事件处理程序使用但仍绑定到该类的方法。 它看起来像这样:

 class Counter { counter = 0; handleClick = () => {   this.counter++; } } 

使用类似的构造,即使事件处理程序调用了handleClick函数,而不是在Counter类的实例的上下文中,该函数也可以访问该实例的数据。

但是,这种方法在此材料上有很多缺点。

尽管这里使用箭头函数固然是绑定函数的便捷方法,但该函数在许多方面的行为都远非直观,会干扰测试并在例如试图将相应对象用作原型的情况下产生问题。

在这种情况下,请使用普通函数代替箭头函数,并在必要时将其绑定到构造函数中的对象实例:

 class Counter { counter = 0; handleClick() {   this.counter++; } constructor() {   this.handleClick = this.handleClick.bind(this); } } 

call长通话链


如果计划将箭头功能用于许多不同的组合中,尤其是在长函数调用链中使用,则箭头功能可能会成为问题的根源。

出现此类问题的主要原因(例如使用匿名函数时)是,它们给出了调用堆栈跟踪的极其无用的结果。

例如,如果仅存在一层嵌套的函数调用(例如,如果我们谈论的是迭代器中使用的函数),这并不太糟糕。 但是,如果所有使用的函数都声明为箭头函数,并且这些函数相互调用,那么如果发生错误,将很难弄清楚正在发生的情况。 错误消息将如下所示:

 {anonymous}() {anonymous}() {anonymous}() {anonymous}() {anonymous}() 

with具有动态上下文的功能


我们讨论的最后一种情况(箭头功能可能会引起麻烦)是在需要动态this绑定的地方使用它们。

如果在这种情况下使用了箭头功能,则动态绑定将无法工作。 这种令人不愉快的惊奇可能使人们困惑那些不得不使用箭头功能使用不正确的代码的人会发生什么情况的原因。

考虑使用箭头功能时,请记住以下几点:

  • this绑定到currentTarget事件属性的事件处理程序将被调用。
  • 如果仍在使用jQuery,请考虑大多数jQuery方法this绑定到选定的DOM元素。
  • 如果使用Vue.js,则方法和计算函数通常会将其绑定到Vue组件。

当然,可以有意使用箭头功能,以更改软件机制的标准行为。 但是,尤其是在使用jQuery和Vue的情况下,这通常与系统的正常功能相冲突,这导致程序员无法理解为什么某些看上去完全正常的代码突然无法工作的事实。

总结


箭头功能是一个很棒的JavaScript新功能。 在许多情况下,它们允许编写比以前更方便的代码。 但是,与其他功能一样,它们既有优点也有缺点。 因此,您需要在可能有用的地方使用箭头功能,而不必将其视为普通功能的完全替代。

亲爱的读者们! 您是否遇到过使用箭头功能导致程序错误,不便或意外行为的情况?

Source: https://habr.com/ru/post/zh-CN428566/


All Articles