23.可调用值

原文: http://exploringjs.com/impatient-js/ch_callables.html

23.1。各种功能

JavaScript 有两类功能:

  • 普通函数 可以扮演几个角色:
    • 实际功能(在其他语言中,你只需使用术语“功能”;在 JavaScript 中,我们需要区分角色“真实功能”和可以扮演该角色的实体“普通功能”)
    • 方法
    • 构造函数
  • 专用功能 只能扮演其中一个角色。例如:
    • 箭头功能 只能是实际功能。
    • 方法 只能是一种方法。
    • 只能是构造函数。

接下来的部分将解释所有这些内容的含义。

23.2。普通功能

以下代码显示了三种方法(大致)相同的事情:创建一个普通的函数。

正如我们在中看到的关于变量的章节,函数声明被提升,而变量声明(例如通过const)则没有。我们将在本章后面探讨其后果。

函数声明和函数表达式的语法非常相似。上下文决定哪个是哪个。有关这种语法歧义的更多信息,请参阅关于语法的章节。

23.2.1。函数声明的一部分

让我们通过一个例子来检查函数声明的各个部分:

  • add是函数声明的 名称
  • add(x, y)是函数声明的
  • xy 参数
  • 花括号({})以及它们之间的所有东西都是函数声明的
  • return运算符显式返回函数的值。

23.2.2。普通功能的名称

函数表达式的名称只能在函数内部访问,函数可以使用它来引用自身(例如,用于自递归):

相反,函数声明的名称可在当前范围内访问:

23.2.3。普通功能扮演的角色

请考虑上一节中的以下函数声明:

该函数声明创建一个名为add的普通函数。作为一个普通的功能,add()可以扮演三个角色:

  • 实函数:通过函数调用调用。这是大多数编程语言认为简单的 函数

  • 方法:存储在属性中,通过方法调用调用。

  • 构造函数/类:通过new调用。

    (顺便说一句,类的名称通常以大写字母开头。)

23.3。专业功能

专业功能是普通功能的专用版本。他们每个人只扮演一个角色:

  • 箭头函数 只能是一个真正的函数:

  • 方法 只能是一种方法:

  • 只能是构造函数:

除了更好的语法之外,每种专用功能还支持新功能,使其在工作中比普通功能更好。

TBL。 18 列出了普通和专用功能的功能。

Table 18: Capabilities of four kinds of functions.

普通功能 箭头功能 方法
函数调用
方法调用 词汇this
构造函数调用

23.3.1。专业功能仍然是功能

值得注意的是,箭头函数,方法和类仍然归类为函数:

23.3.2。建议:更喜欢专门的功能

通常,您应该优先使用专用函数而不是普通函数,尤其是类和方法。但是,箭头功能和普通功能之间的选择不那么明确:

  • 箭头函数没有this作为隐式参数。如果你使用真正的函数,这几乎总是你想要的,因为它避免了一个重要的this相关的陷阱(详见关于单个对象的章节)。

  • 但是,我喜欢语法上的函数声明(它产生一个普通的函数)。如果你不在其中使用this,它大部分相当于const加箭头功能:

23.3.3。箭头功能

Arrow 函数被添加到 JavaScript 中有两个原因:

  1. 为创建函数提供更简洁的方法。
  2. 更容易使用实际函数:不能在普通函数中引用周围范围的this(详见很快)。
23.3.3.1。箭头函数的语法

让我们回顾一下匿名函数表达式的语法:

(粗略)等效箭头函数如下所示。箭头函数是表达式。

这里,箭头函数的主体是一个块。但它也可以是一种表达方式。以下箭头功能与前一个功能完全相同。

如果箭头函数只有一个参数且该参数是标识符(不是解构模式),那么您可以省略参数周围的括号:

将箭头函数作为参数传递给其他函数或方法时,这很方便:

最后一个例子展示了箭头功能的第一个好处 - 简洁。相反,这是相同的方法调用,但带有函数表达式:

23.3.3.2。箭头功能:词汇this

普通函数既可以是方法,也可以是实际函数。唉,这两个角色是冲突的:

  • 由于每个普通函数都可以是一个方法,因此它有自己的this
  • 自己的this使得无法从普通函数内部访问周围范围的this。这对于真正的功能来说是不方便的。

以下代码演示了一种常见的解决方法:

在 B 行中,我们想要访问.prefixStringArray()this。但我们不能,因为周围的普通函数有自己的this 阴影 (阻止访问)方法的this。因此,我们将方法的this保存在额外变量that(A 行)中,并在 B 行中使用该变量。

箭头函数没有this作为隐式参数,它从周围环境中获取其值。也就是说,this的行为与任何其他变量一样。

总结一下:

  • 在普通函数中,this是隐式( 动态 )参数(中有关单个对象的章节中的详细信息)。
  • 箭头函数从其周围的范围获得this 词法 )。
23.3.3.3。语法陷阱:从箭头函数返回一个对象字面值

如果希望箭头函数的表达式主体是对象字面值,则必须将字面值放在括号中:

如果不这样,JavaScript 认为,箭头函数有一个块体(不返回任何内容):

{a: 1}被解释为标签a: 和表达式语句1的块。

这个陷阱是由语法模糊引起的:对象字面值和代码块具有相同的语法,我们必须帮助 JavaScript 区分它们。

23.4。吊装功能

函数声明是 悬挂 (内部移动到顶部):

提升允许您在声明之前调用foo()

变量声明不会被挂起:在以下示例中,您只能在声明后使用bar()

类声明不会悬挂,也可以:

23.4.1。在没有吊装的情况下提前召唤

注意函数f()仍然可以在声明之前调用非提升函数g() - 如果在声明g()之后调用f()

通常在执行模块的完整主体之后调用模块的功能。因此,您很少需要担心模块中的函数顺序。

23.4.2。吊装的陷阱

如果您在声明之前依赖于提升来调用函数,那么您需要注意它不能访问非提升数据。

和以前一样,如果你在最后调用函数hoistedFunc(),问题就会消失。

23.5。从函数返回值

使用return运算符从函数返回值:

另一个例子:

如果在函数末尾没有显式返回任何内容,JavaScript 会为您返回undefined

23.6。参数处理

23.6.1。术语:参数与参数

术语 参数 和术语 参数 基本上意思相同。如果您愿意,可以进行以下区分:

  • 参数 是函数定义的一部分。它们也被称为 形式参数 形式参数

  • 参数 是函数调用的一部分。它们也被称为 实际参数 实际参数

23.6.2。术语:回调

回调 回调函数 是作为参数传递给另一个函数或方法的函数。此术语经常在 JavaScript 社区中广泛使用。

以下是回调的示例:

23.6.3。参数太多或不够

如果函数调用提供的参数数量不同于函数定义所预期的数量,则 JavaScript 不会抱怨:

  • 额外的参数被忽略。
  • 缺少的参数设置为undefined

例如:

23.6.4。参数默认值

参数默认值指定在未提供参数时要使用的值。例如:

undefined也会触发默认值:

23.6.5。休息参数

通过在标识符前面添加三个点(...)来声明 rest 参数。在函数或方法调用期间,它接收包含所有剩余参数的数组。如果最后没有额外的参数,那么它是一个空数组。例如:

23.6.5.1。通过 rest 参数强制执行一定数量的参数

您可以使用 rest 参数来强制执行一定数量的参数。例如,以下功能。

这就是我们强制调用者总是提供两个参数的方法:

23.6.6。命名参数

当有人调用函数时,调用者提供的参数将分配给被调用者接收的参数。执行映射的两种常用方法是:

  1. 位置参数:如果参数具有相同的位置,则将参数分配给参数。仅具有位置参数的函数调用如下所示。

  2. 命名参数:如果参数具有相同的名称,则将参数分配给参数。 JavaScript 没有命名参数,但您可以模拟它们。例如,这是一个只有(模拟)命名参数的函数调用:

命名参数有几个好处:

  • 它们导致更加自我解释的代码,因为每个参数都有一个描述性标签。只需比较selectEntries()的两个版本:使用第二个版本,可以更容易地看到发生了什么。

  • 参数顺序无关紧要(只要名称正确)。

  • 处理多个可选参数更方便:调用者可以轻松提供所有可选参数的任何子集,而不必知道它们省略的那些(使用位置参数,您必须使用undefined填写前面的可选参数])。

23.6.7。模拟命名参数

JavaScript 没有真正的命名参数。模拟它们的官方方法是通过对象字面值:

此函数使用 解构 来访问其单个参数的属性。它使用的模式是以下模式的缩写:

这种解构模式适用于空对象字面值:

但是如果你在没有任何参数的情况下调用函数它就不起作用:

您可以通过为整个模式提供默认值来解决此问题。此默认值与更简单的参数定义的默认值相同:如果缺少该参数,则使用默认值。

23.6.8。将(...)传播到函数调用中

spread 参数的前缀(...)与 rest 参数的前缀相同。在调用函数或方法时使用前者。它的操作数必须是可迭代的对象。迭代的值转换为位置参数。例如:

因此,扩展参数和 rest 参数用于相反的目的:

  • 定义函数或方法时使用 Rest 参数。他们在数组中收集参数。
  • 调用函数或方法时使用 Spread 参数。他们将可迭代对象转换为参数。
23.6.8.1。示例:传播到Math.max()

Math.max()返回其零个或多个参数中最大的一个。唉,它不能用于数组,但传播给了我们一条出路:

23.6.8.2。示例:传播到Array.prototype.push()

类似地,Array 方法.push()破坏性地将其零个或多个参数添加到其 Array 的末尾。 JavaScript 没有破坏性地将数组附加到另​​一个数组的方法,但我们再次通过传播保存:

23.可调用值 - 图1 练习:参数处理

  • 位置参数:exercises/callables/positional_parameters_test.js
  • 命名参数:exercises/callables/named_parameters_test.js

23.可调用值 - 图2 测验

参见测验应用程序