甲功能是定义一次和多次调用以后的代码的参数块。在JavaScript中,功能由许多组件组成并受其影响:
- 构成函数主体的JavaScript代码
- 参数清单
- 可从词汇范围访问的变量
- 返回值
this
调用函数时的上下文- 命名或匿名函数
- 包含函数对象的变量
arguments
对象(或缺少箭头功能)
这篇文章教您六种声明JavaScript函数的方法:语法,示例和常见陷阱。此外,您将了解在某些情况下何时使用特定功能类型。
1.函数声明
函数声明由function
关键字组成,后跟一个强制性的函数名称,一对括号中的参数列表(para1, ..., paramN)
以及一对{...}
界定主体代码的花括号。
函数声明的示例:
function isEven(num) {...}
是定义isEven
函数的函数声明,该函数确定数字是否为偶数。
函数声明在当前作用域中创建一个变量,其标识符等于函数名称。此变量保存功能对象。
函数变量被提升到当前作用域的顶部,这意味着可以在声明之前调用该函数(有关更多详细信息,请参见本章)。
创建的函数名为,这意味着name
函数对象的属性保留其名称。在查看调用堆栈时非常有用:在调试或读取错误消息时。
让我们在示例中查看这些属性:
函数声明function hello(name) {...}
创建一个变量hello
,该变量将提升到当前作用域的顶部。hello
变量保存函数对象并hello.name
包含函数名称:'hello'
。
1.1常规功能
函数声明与需要常规函数的情况匹配。常规表示您一次声明该函数,然后在许多不同的地方调用它。这是基本方案:
由于函数声明会在常规范围内与常规函数调用一起在当前作用域中创建一个变量,因此对于递归或分离事件侦听器很有用。与函数表达式或箭头函数相反,后者不按名称创建与函数变量的绑定。
例如,要递归计算阶乘,您必须访问内部的函数:
内部factorial()
递归调用正在使用保持着该函数的变量做:factorial(n - 1)
。
可以使用函数表达式并将其分配给正则变量,例如const factorial = function(n) {...}
。但是函数声明function factorial(n)
很紧凑(不需要const
和=
)。
函数声明的一个重要属性是它的提升机制。它允许在相同作用域中的声明之前使用该函数。
提升在某些情况下很有用。例如,当您想查看在脚本开头如何调用函数而无需阅读函数实现时。函数实现可以位于文件的下面,因此您甚至不能滚动到那里。
您可以在此处阅读有关函数声明提升的更多详细信息。
1.2与函数表达式的区别
容易混淆函数声明和函数表达式。它们看起来非常相似,但是产生的功能具有不同的属性。
一个容易记住的规则:语句中的函数声明始终以关键字开头function
。否则,它是一个函数表达式(请参见2.)。
以下示例是一个函数声明,其中的语句以关键字开头function
:
对于函数表达式,JavaScript语句不是以function
关键字开头的(它出现在语句代码的中间):
1.3有条件的函数声明
调用其声明块中出现的功能时,某些JavaScript环境可以抛出一个引用错误{...}
的if
,for
或while
陈述。
让我们启用严格模式,看看在条件中声明一个函数会发生什么:
调用时ok()
,JavaScript抛出ReferenceError: ok is not defined
,因为函数声明位于条件块内。
在非严格模式下允许有条件的函数声明,这使它更加混乱。
对于这些情况的一般规则是,当应根据条件创建函数时,请使用函数表达式。让我们看看这是怎么可能的:
由于函数是常规对象,因此请根据条件将其分配给变量。调用ok()
工作正常,没有错误。
2.函数表达式
函数表达式由function
关键字确定,后跟一个可选的函数名称,一对括号中的参数列表(para1, ..., paramN)
以及一对{ ... }
界定主体代码的花括号。
函数表达式的一些示例:
函数表达式创建一个可以在不同情况下使用的函数对象:
- 分配给变量作为对象
count = function(...) {...}
- 在对象上创建方法
sum: function() {...}
- 使用该函数作为回调
.reduce(function(...) {...})
函数表达式是JavaScript中的主力军。通常,您需要在箭头函数旁边处理这种类型的函数声明(如果您喜欢简短的语法和词法上下文)。
2.1命名函数表达式
当函数没有名称(name
属性为空字符串''
)时,它是匿名的:
这是一个匿名函数,名称是一个空字符串。
有时可以推断出函数名称。例如,当匿名变量分配给变量时:
匿名函数名称为'myFunctionVar'
,因为myFunctionVar
变量名用于推断函数名称。
当表达式具有指定的名称时,这是一个命名函数表达式。与简单函数表达式相比,它具有一些其他属性:
- 创建一个命名函数,即
name
属性包含函数名称 - 在函数体内,具有相同名称的变量保存函数对象
让我们使用上面的示例,但是在函数表达式中设置一个名称:
function funName(variable) {...}
是一个命名函数表达式。该变量funName
可在函数范围内访问,但不能在外部访问。无论哪种方式,name
功能对象的属性都具有名称:funName
。
2.2偏爱命名函数表达式
将函数表达式const fun = function() {}
分配给变量后,某些引擎会从此变量推断函数名称。但是,回调可能作为匿名函数表达式传递而没有存储到变量中:因此引擎无法确定其名称。
赞成使用命名函数并避免使用匿名函数来获得诸如以下的好处是合理的:
- 使用函数名称时,错误消息和调用堆栈显示更多详细信息
- 通过减少匿名堆栈名称的数量来进行更舒适的调试
- 函数名称说明函数的作用
- 您可以在函数范围内访问该函数以进行递归调用或分离事件侦听器
3.速记方法定义
速记方法定义可用于对象常量和ES2015类的方法声明中。您可以使用函数名称定义它们,然后在一对括号(para1, ..., paramN)
和一对{ ... }
界定主体语句的花括号中使用参数列表。
下面的示例在对象文字中使用速记方法定义:
add()
使用简短的方法定义来定义对象中的和get()
方法collection
。照常调用这些方法:collection.add(...)
和collection.get(...)
。
与使用名称,冒号:
和函数表达式的传统属性定义相比,方法定义的简短方法具有多个优点add: function(...) {...}
:
- 较短的语法更易于理解
- 与函数表达式相反,速记方法定义将创建一个命名函数。这对于调试很有用。
该class
语法要求采用简短形式的方法声明:
3.1计算的属性名称和方法
ECMAScript 2015新增了一个不错的功能:对象文字和类中的计算属性名称。
计算的属性使用略有不同的语法[methodName]() {...}
,因此方法定义看起来是这样的:
[addMethod](...) {...}
并 [getMethod](...) {...}
与计算的属性名称的简写方法声明。
4.箭头功能
箭头函数是使用一对圆括号定义的,圆括号中包含参数列表(param1, param2, ..., paramN)
,然后是粗体箭头=>
和一对大括号{...}
,用于分隔主体语句。
当箭头功能只有一个参数时,一对括号可以省略。当它包含单个语句时,花括号也可以省略。
让我们看看箭头函数的基本用法:
absValue
是一个箭头函数,用于计算数字的绝对值。
使用粗箭头声明的函数具有以下属性:
- 箭头函数不会创建其执行上下文,而是以词法接受(与函数表达式或函数声明相反,后者
this
取决于调用而创建自己的函数) - 箭头函数是匿名的。但是,引擎可以从保存该函数的变量中推断出其名称。
arguments
object在箭头函数中不可用(与其他提供arguments
对象的声明类型相反)。不过,您可以自由使用rest参数(...params)
。
4.1上下文透明
this
关键字是JavaScript的一个令人困惑的方面(请参阅本文以获取有关的详细说明this
)。
由于函数创建自己的执行上下文,因此通常很难检测到this
值。
ECMAScript 2015 this
通过引入箭头功能来改进用法,该功能以词法(或直接this
从直接外部作用域使用)获取上下文。这很好,因为当函数需要封闭.bind(this)
的上下文var self = this
时,您不必使用或存储上下文。
让我们看看如何this
从外部函数继承:
Numbers
类包含一个数字数组,并提供一种addNumber()
插入新数字的方法。
当addNumber()
不带参数的情况下调用时,将返回一个允许插入数字的闭包。该闭合是具有箭头功能this
如numbersObject
,因为上下文从词法采取实例addNumbers()
方法。
如果没有箭头功能,则必须手动修复上下文。这意味着使用类似.bind()
方法的解决方法:
或将上下文存储到单独的变量中var self = this
:
如果要保持this
原样(从封闭上下文中获取),可以使用上下文透明性。
4.2短回调
创建箭头函数时,括号对和花括号对于单个参数和单个主体语句是可选的。这有助于创建非常短的回调函数。
让我们创建一个函数,该函数查找数组是否包含0
:
item => item === 0
是一个箭头函数,看起来很简单。
请注意,嵌套的短箭头功能很难阅读。使用最短箭头函数形式的便捷方法是单个回调(无嵌套)。
如有必要,在编写嵌套箭头函数时,请使用箭头函数的扩展语法。它更容易阅读。
5.发电机功能
JavaScript中的generator函数返回Generator对象。它的语法类似于函数表达式,函数声明或方法声明,只是它需要一个星号*
。
生成器函数可以用以下形式声明:
一个。函数声明形式function* <name>()
:
b。函数表达形式function* ()
:
C。速记方法定义表*<name>()
:
在这3种情况下,generator函数都将返回generator对象g
。以后g
用于生成一系列递增的数字。
6.另一件事:新功能
在JavaScript中,函数是一类对象-函数是type的常规对象function
。
上面描述的声明方式创建了相同的函数对象类型。让我们来看一个例子:
函数对象类型具有一个构造函数:Function
。
当Function
作为构造new Function(arg1, arg2, ..., argN, bodyString)
函数调用时,将创建一个新函数。这些参数arg1, args2, ..., argN
传递给构造成为新的函数的参数名称和最后一个参数bodyString
作为函数体代码。
让我们创建一个将两个数字相加的函数:
sumFunction
使用Function
构造函数调用创建的对象具有参数numberA
和numberB
和主体return numberA + numberB
。
用这种方法创建的函数无法访问当前作用域,因此无法创建闭包。它们始终在全局范围内创建。
一种可能的应用new Function
是在浏览器或NodeJS脚本中访问全局对象的更好方法:
请记住,几乎绝对不应使用函数声明函数new Function()
。由于功能主体是在运行时评估的,因此该方法继承了许多eval()
使用问题:安全隐患,调试困难,无法应用引擎优化,无法自动完成编辑器。
7.最后,哪种方法更好?
没有赢家或宽松者。选择哪种声明类型取决于情况。
在某些情况下,您可能需要遵循一些规则。
如果该函数使用this
自封闭函数,则箭头函数是一个很好的解决方案。当回调函数有一个简短的语句时,箭头函数也是一个不错的选择,因为它会创建简短的代码。
对于在对象文字上声明方法的语法较短的情况,速记方法声明是可取的。
new Function
通常不应使用声明函数的方法。主要是因为它带来了潜在的安全风险,不允许代码在编辑器中自动完成,并且丢失了引擎优化功能。
原著作者:德米特里·帕夫鲁汀
文章来源:国外
原著链接:
Dmitri Pavlutin Blogdmitripavlutin.comPS:原著文章内容为英文版本,建议使用360极速浏览器进行翻译阅读。