了解JavaScript中的箭头函数

The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

作者选择了COVID-19救济基金来接受捐赠,这是Write for DOnations计划的一部分。

介绍 (Introduction)

The 2015 edition of the ECMAScript specification (ES6) added arrow function expressions to the JavaScript language. Arrow functions are a new way to write anonymous function expressions, and are similar to lambda functions in some other programming languages, such as Python.

2015年版ECMAScript规范(ES6)JavaScript语言添加了箭头函数表达式 。 箭头函数是一种编写匿名函数表达式的新方法,类似于某些其他编程语言(例如Python)中的 lambda函数

Arrow functions differ from traditional functions in a number of ways, including the way their scope is determined and how their syntax is expressed. Because of this, arrow functions are particularly useful when passing a function as a parameter to a higher-order function, such as when you are looping over an array with built-in iterator methods. Their syntactic abbreviation can also allow you to improve the readability of your code.

箭头函数在许多方面与传统函数不同,包括确定其范围的方式以及如何表达其语法。 因此,在将函数作为参数传递给高阶函数时(例如,使用内置迭代器方法遍历数组时),箭头函数特别有用。 它们的语法缩写也可以使您提高代码的可读性。

In this article, you will review function declarations and expressions, learn about the differences between traditional function expressions and arrow function expressions, learn about lexical scope as it pertains to arrow functions, and explore some of the syntactic shorthand permitted with arrow functions.

在本文中,您将复习函数声明和表达式,了解传统函数表达式和箭头函数表达式之间的区别,了解与箭头函数有关的词法范围,并探索一些箭头函数允许的语法速记。

定义功能 (Defining Functions)

Before delving into the specifics of arrow function expressions, this tutorial will briefly review traditional JavaScript functions in order to better show the unique aspects of arrow functions later on.

在深入研究箭头函数表达式的细节之前,本教程将简要回顾传统JavaScript函数,以便在以后更好地显示箭头函数的独特方面。

The How To Define Functions in JavaScript tutorial earlier in this series introduced the concept of function declarations and function expressions. A function declaration is a named function written with the function keyword. Function declarations load into the execution context before any code runs. This is known as hoisting, meaning you can use the function before you declare it.

本系列前面的“ 如何在JavaScript中定义函数”教程介绍了函数声明函数表达式的概念。 函数声明是使用function关键字编写的命名函数。 在任何代码运行之前,函数声明会加载到执行上下文中。 这称为提升 ,意味着可以在声明函数之前使用它。

Here is an example of a sum function that returns the sum of two parameters:

这是一个sum函数的示例,该函数返回两个参数的和:

function sum(a, b) {
  return a + b
}

You can execute the sum function before declaring the function due to hoisting:

由于吊起,可以在声明函数之前执行sum函数:

sum(1, 2)

function sum(a, b) {
  return a + b
}

Running this code would give the following output:

运行此代码将给出以下输出:


   
   
Output
3

You can find the name of the function by logging the function itself:

您可以通过记录函数本身来找到函数的名称:

console.log(sum)

This will return the function, along with its name:

这将返回函数及其名称:


   
   
Output
ƒ sum(a, b) { return a + b }

A function expression is a function that is not pre-loaded into the execution context, and only runs when the code encounters it. Function expressions are usually assigned to a variable, and can be anonymous, meaning the function has no name.

函数表达式是未预加载到执行上下文中的函数,仅在代码遇到该函数时才运行。 函数表达式通常分配给变量,并且可以是匿名的 ,这意味着函数没有名称。

In this example, write the same sum function as an anonymous function expression:

在此示例中,编写与匿名函数表达式相同的sum函数:

const sum = function (a, b) {
  return a + b
}

You’ve now assigned the anonymous function to the sum constant. Attempting to execute the function before it is declared will result in an error:

现在,您已将匿名函数分配给sum常量。 尝试在声明函数之前执行该函数将导致错误:

sum(1, 2)

const sum = function (a, b) {
  return a + b
}

Running this will give:

运行此命令将得到:


   
   
Output
Uncaught ReferenceError: Cannot access 'sum' before initialization

Also, note that the function does not have a named identifier. To illustrate this, write the same anonymous function assigned to sum, then log sum to the console:

另外,请注意,该函数没有命名标识符。 为了说明这一点,写分配到同一个匿名函数sum ,然后登录sum控制台:

const sum = function (a, b) {
  return a + b
}

console.log(sum)

This will show you the following:

这将显示以下内容:


   
   
Output
ƒ (a, b) { return a + b }

The value of sum is an anonymous function, not a named function.

sum的值是一个匿名函数,而不是一个命名函数。

You can name function expressions written with the function keyword, but this is not popular in practice. One reason you might want to name a function expression is to make error stack traces easier to debug.

您可以命名用function关键字编写的函数表达式,但这在实践中并不流行。 您可能要命名函数表达式的一个原因是使错误堆栈跟踪更易于调试。

Consider the following function, which uses an if statement to throw an error if the function parameters are missing:

考虑以下函数, if缺少函数参数,该函数将使用if语句引发错误:

const sum = function namedSumFunction(a, b) {
  if (!a || !b) throw new Error('Parameters are required.')

  return a + b
}

sum();

The highlighted section gives the function a name, and then the function uses the or || operator to throw an error object if either of the parameters is missing.

突出显示的部分为函数命名,然后函数使用 || 如果缺少任何一个参数,运算符将引发错误对象

Running this code will give you the following:

运行此代码将为您提供以下内容:


   
   
Output
Uncaught Error: Parameters are required. at namedSumFunction (<anonymous>:3:23) at <anonymous>:1:1

In this case, naming the function gives you a quick idea of where the error is.

在这种情况下,命名功能可以使您快速了解错误的位置。

An arrow function expression is an anonymous function expression written with the “fat arrow” syntax (=>).

箭头函数表达式是使用“胖箭头”语法( => )编写的匿名函数表达式。

Rewrite the sum function with arrow function syntax:

用箭头函数语法重写sum函数:

const sum = (a, b) => {
  return a + b
}

Like traditional function expressions, arrow functions are not hoisted, and so you cannot call them before you declare them. They are also always anonymous—there is no way to name an arrow function. In the next section, you will explore more of the syntactical and practical differences between arrow functions and traditional functions.

像传统的函数表达式一样,箭头函数不会被吊起,因此在声明它们之前不能调用它们。 它们也始终是匿名的-无法命名箭头函数。 在下一节中,您将探索箭头功能与传统功能之间在语法和实践上的更多差异。

箭头功能行为和语法 (Arrow Function Behavior and Syntax)

Arrow functions have a few important distinctions in how they work that distinguish them from traditional functions, as well as a few syntactic enhancements. The biggest functional differences are that arrow functions do not have their own this binding or prototype and cannot be used as a constructor. Arrow functions can also be written as a more compact alternative to traditional functions, as they grant the ability to omit parentheses around parameters and add the concept of a concise function body with implicit return.

箭头功能在工作方式上有一些重要区别,这些区别使其与传统功能有所不同,并且在语法上进行了一些增强。 最大的功能性区别是箭头的功能没有自己的this结合或原型,不能用作构造函数。 箭头函数也可以写为传统函数的更紧凑的替代方案,因为它们可以省略参数周围的括号并添加具有隐式返回的简洁函数体的概念。

In this section, you will go through examples that illustrate each of these cases.

在本节中,您将通过示例说明每种情况。

词汇this (Lexical this)

The keyword this is often considered a tricky topic in JavaScript. The article Understanding This, Bind, Call, and Apply in JavaScript explains how this works, and how this can be implicitly inferred based on whether the program uses it in the global context, as a method within an object, as a constructor on a function or class, or as a DOM event handler.

关键字this在JavaScript中通常被认为是一个棘手的话题。 文章了解这一点,绑定,呼叫,并在JavaScript应用解释如何this工作,以及如何this可以隐式推断基于程序是否使用它在全球范围内,作为一个对象中的方法,作为一个功能的构造或类,或作为DOM事件处理程序。

Arrow functions have lexical this, meaning the value of this is determined by the surrounding scope (the lexical environment).

箭头函数具有词法this ,意的值this是通过周边范围(词法环境)测定。

The next example will demonstrate the difference between how traditional and arrow functions handle this. In the following printNumbers object, there are two properties: phrase and numbers. There is also a method on the object, loop, which should print the phrase string and the current value in numbers:

下一个示例将演示传统函数和箭头函数处理this的区别。 在以下printNumbers对象中,有两个属性: phrasenumbers 。 在对象loop上还有一个方法,该方法应该以numbers打印phrase字符串和当前值:

const printNumbers = {
  phrase: 'The current value is:',
  numbers: [1, 2, 3, 4],

  loop() {
    this.numbers.forEach(function (number) {
      console.log(this.phrase, number)
    })
  },
}

One might expect the loop function to print the string and current number in the loop on each iteraton. However, in the result of running the function the phrase is actually undefined:

可能希望loop函数在每个iteraton上的loop中打印字符串和当前数字。 但是,在运行函数的结果中,该phrase实际上是undefined

printNumbers.loop()

This will give the following:

这将给出以下内容:


   
   
Output
undefined 1 undefined 2 undefined 3 undefined 4

As this shows, this.phrase is undefined, indicating that this within the anonymous function passed into the forEach method does not refer to the printNumbers object. This is because a traditional function will not determine its this value from the scope of the environment, which is the printNumbers object.

作为该显示, this.phrase是未定义的,表明this传递到匿名函数内forEach方法并不指printNumbers对象。 这是因为传统函数不会从环境范围(即printNumbers对象)确定this值。

In older versions of JavaScript, you would have had to use the bind method, which explicitly sets this. This pattern can be found often in some earlier versions of frameworks, like React, before the advent of ES6.

在旧版JavaScript中,您将不得不使用bind方法,该方法显式设置this 。 这种模式通常可以在ES6出现之前的一些早期版本的框架中找到,例如React

Use bind to fix the function:

使用bind修复功能:

const printNumbers = {
  phrase: 'The current value is:',
  numbers: [1, 2, 3, 4],

  loop() {
    // Bind the `this` from printNumbers to the inner forEach function
    this.numbers.forEach(
      function (number) {
        console.log(this.phrase, number)
      }.bind(this),
    )
  },
}

printNumbers.loop()

This will give the expected result:

这将得到预期的结果:


   
   
Output
The current value is: 1 The current value is: 2 The current value is: 3 The current value is: 4

Arrow functions provide a more direct way of dealing with this. Since their this value is determined based on the lexical scope, the inner function called in forEach can now access the properties of the outer printNumbers object, as demonstrated:

箭头函数提供了一种更直接的方式来处理此问题。 由于this值是根据词法范围确定的,因此在forEach调用的内部函数现在可以访问外部printNumbers对象的属性,如所示:

const printNumbers = {
  phrase: 'The current value is:',
  numbers: [1, 2, 3, 4],

  loop() {
    this.numbers.forEach((number) => {
      console.log(this.phrase, number)
    })
  },
}

printNumbers.loop()

This will give the expected result:

这将得到预期的结果:


   
   
Output
The current value is: 1 The current value is: 2 The current value is: 3 The current value is: 4

These examples establish that using arrow functions in built-in array methods like forEach, map, filter, and reduce can be more intuitive and easier to read, making this strategy more likely to fulfill expectations.

这些示例证明,在内置数组方法(例如forEachmapfilterreduce中使用箭头函数可以更直观,更易于阅读,从而使该策略更有可能满足期望。

箭头功能用作对象方法 (Arrow Functions as Object Methods)

While arrow functions are excellent as parameter functions passed into array methods, they are not effective as object methods because of the way they use lexical scoping for this. Using the same example as before, take the loop method and turn it into an arrow function to discover how it will execute:

尽管箭头函数非常适合作为传递给数组方法的参数函数使用,但由于它们this使用词法作用域的方式,因此它们不能有效地用作对象方法。 使用与之前相同的示例,采用loop方法并将其转换为箭头函数以发现其执行方式:

const printNumbers = {
  phrase: 'The current value is:',
  numbers: [1, 2, 3, 4],

  loop: () => {
    this.numbers.forEach((number) => {
      console.log(this.phrase, number)
    })
  },
}

In this case of an object method, this should refer to properties and methods of the printNumbers object. However, since an object does not create a new lexical scope, an arrow function will look beyond the object for the value of this.

在这种情况下物体的方法的, this应该是指性质和方法printNumbers对象。 但是,由于对象不会创建新的词法作用域,因此箭头函数将在对象之外寻找this的值。

Call the loop() method:

调用loop()方法:

printNumbers.loop()

This will give the following:

这将给出以下内容:


   
   
Output
Uncaught TypeError: Cannot read property 'forEach' of undefined

Since the object does not create a lexical scope, the arrow function method looks for this in the outer scope–Window in this example. Since the numbers property does not exist on the Window object, it throws an error. As a general rule, it is safer to use traditional functions as object methods by default.

由于对象不创建词法作用域,箭头函数方法查找this外scope- Window在这个例子。 由于numbers属性在Window对象上不存在,因此会引发错误。 通常,默认情况下将传统函数用作对象方法会更安全。

箭头函数没有constructorprototype (Arrow Functions Have No constructor or prototype)

The Understanding Prototypes and Inheritance in JavaScript tutorial earlier in this series explained that functions and classes have a prototype property, which is what JavaScript uses as a blueprint for cloning and inheritance.

本系列前面的理解JavaScript中的原型和继承教程介绍了函数和类具有prototype属性,JavaScript将其用作克隆和继承的蓝图。

To illustrate this, create a function and log the the automatically assigned prototype property:

为了说明这一点,创建一个函数并记录自动分配的prototype属性:

function myFunction() {
  this.value = 5
}

// Log the prototype property of myFunction
console.log(myFunction.prototype)

This will print the following to the console:

这会将以下内容打印到控制台:


   
   
Output
{constructor: ƒ}

This shows that in the prototype property there is an object with a constructor. This allows you to use the new keyword to create an instance of the function:

这表明在prototype属性中存在一个带有constructor的对象。 这使您可以使用new关键字创建该函数的实例:

const instance = new myFunction()

console.log(instance.value)

This will yield the value of the value property that you defined when you first declared the function:

这将产生您在首次声明该函数时定义的value属性的value


   
   
Output
5

In contrast, arrow functions do not have a prototype property. Create a new arrow function and try to log its prototype:

相反,箭头函数没有prototype属性。 创建一个新的箭头函数,并尝试记录其原型:

const myArrowFunction = () => {}

// Attempt to log the prototype property of myArrowFunction
console.log(myArrowFunction.prototype)

This will give the following:

这将给出以下内容:


   
   
Output
undefined

As a result of the missing prototype property, the new keyword is not available and you cannot construct an instance from the arrow function:

由于缺少prototype属性,因此new关键字不可用,并且无法通过arrow函数构造实例:

const arrowInstance = new myArrowFunction()

console.log(arrowInstance)

This will give the following error:

这将产生以下错误:


   
   
Output
Uncaught TypeError: myArrowFunction is not a constructor

This is consistent with our earlier example: Since arrow functions do not have their own this value, it follows that you would be unable to use an arrow function as a constructor.

这与我们之前的示例一致:由于箭头函数没有自己的this值,因此您将无法使用箭头函数作为构造函数。

As shown here, arrow functions have a lot of subtle changes that make them operate differently from traditional functions in ES5 and earlier. There have also been a few optional syntactical changes that make writing arrow functions quicker and less verbose. The next section will show examples of these syntax changes.

如此处所示,箭头功能有许多细微的变化,使其与ES5及更早版本中的传统功能有所不同。 还进行了一些可选的语法更改,这些更改使编写箭头功能更快,更省力。 下一节将显示这些语法更改的示例。

隐式回报 (Implicit Return)

The body of a traditional function is contained within a block using curly brackets {} and ends when the code encounters a return keyword. The following is what this implementation looks like as an arrow function:

传统函数的主体包含在使用大括号{}的块中,并在代码遇到return关键字时结束。 以下是此实现的箭头函数形式:

const sum = (a, b) => {
  return a + b
}

Arrow functions introduce concise body syntax, or implicit return. This allows the omission of the curly brackets and the return keyword.

箭头函数引入简洁的主体语法隐式返回 。 这样可以省略大括号和return关键字。

const sum = (a, b) => a + b

Implicit return is useful for creating succinct one-line operations in map, filter, and other common array methods. Note that both the brackets and the return keyword must be omitted. If you cannot write the body as a one-line return statement, then you will have to use the normal block body syntax.

隐式返回对于在mapfilter和其他常见数组方法中创建简洁的单行操作很有用。 请注意,方括号和return关键字都必须省略。 如果您不能将主体编写为单行return语句,则必须使用常规的块主体语法。

In the case of returning an object, syntax requires that you wrap the object literal in parentheses. Otherwise, the brackets will be treated as a function body and will not compute a return value.

在返回对象的情况下,语法要求您将对象文字用括号括起来。 否则,方括号将被视为函数体,并且不会计算return值。

To illustrate this, find the following example:

为了说明这一点,请找到以下示例:

const sum = (a, b) => ({result: a + b})

sum(1, 2)

This will give the following output:

这将给出以下输出:


   
   
Output
{result: 3}

省略单个参数的括号 (Omitting Parentheses Around a Single Parameter)

Another useful syntactic enhancement is the ability to remove parentheses from around a single parameter in a function. In the following example, the square function only operates on one parameter, x:

另一个有用的语法增强功能是能够从函数中的单个参数周围删除括号。 在以下示例中, square函数仅对一个参数x起作用:

const square = (x) => x * x

As a result, you can omit the parentheses around the parameter, and it will work just the same:

结果,您可以省略参数周围的括号,它的作用将相同:

const square = x => x * x

square(10)

This will give the following:

这将给出以下内容:


   
   
Output
100

Note that if a function takes no parameters, parentheses will be required:

请注意,如果函数不带参数,则需要括号:

const greet = () => 'Hello!'

greet()

Calling greet() will work as follows:

调用greet()工作方式如下:


   
   
Output
'Hello!'

Some codebases choose to omit parentheses wherever possible, and others choose to always keep parentheses around parameters no matter what, particularly in codebases that use TypeScript and require more information about each variable and parameter. When deciding how to write your arrow functions, check the style guide of the project to which you are contributing.

一些代码库选择在任何可能的地方省略括号,而其他代码库则无论如何都始终在参数周围加上括号,尤其是在使用TypeScript且需要有关每个变量和参数的更多信息的代码库中。 在决定如何编写箭头功能时,请检查您正在贡献的项目的样式指南。

结论 (Conclusion)

In this article, you reviewed traditional functions and the difference between function declarations and function expressions. You learned that arrow functions are always anonymous, do not have a prototype or constructor, cannot be used with the new keyword, and determine the value of this through lexical scope. Finally, you explored the new syntactic enhancements available to arrow functions, such as implicit return and parentheses omission for single parameter functions.

在本文中,您回顾了传统函数以及函数声明和函数表达式之间的区别。 你了解到,箭头函数总是匿名的,没有一个prototypeconstructor ,不能与使用new关键字,并确定价值this通过词法范围。 最后,您探索了箭头函数可用的新语法增强功能,例如单参数函数的隐式返回和括号省略。

For a review of basic functions, read How To Define Functions in JavaScript. To read more about the concept of scope and hoisting in JavaScript, read Understanding Variables, Scope, and Hoisting in JavaScript.

要了解基本功能,请阅读如何在JavaScript中定义功能 。 要阅读有关JavaScript中作用域和提升的概念的更多信息,请阅读了解 JavaScript中的变量,作用域和提升

翻译自: https://www.digitalocean.com/community/tutorials/understanding-arrow-functions-in-javascript

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值