以下是创建函数的标准表单的概要:(最初是为另一个问题编写的,但在被移入规范问题后进行了调整。)
条款:
快速列表:
函数声明
“匿名”function 表达式(尽管有这个术语,但有时会创建带有名称的函数)
命名为function 表达式
访问函数初始化器 (ES5+)
箭头函数表达式 (ES2015+) (与匿名函数表达式一样,不涉及显式名称,但可以创建具有名称的函数)
对象初始化器中的方法声明 (ES2015+)
class (ES2015+) 中的构造函数和方法声明
函数声明
第一种形式是函数声明,如下所示:
function x() {
console.log('x');
}
函数声明是一个声明;它不是一个陈述或表达。因此,您不要使用; 来关注它(尽管这样做是无害的)。
函数声明在执行进入它出现的上下文时被处理,在任何分步代码被执行之前。它创建的函数有一个正确的名称(上例中的x),并且该名称被放置在声明出现的范围内。
因为它是在同一上下文中的任何分步代码之前处理的,您可以执行以下操作:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
在 ES2015 之前,该规范没有涵盖如果将函数声明放入控制结构(如 try、if、switch、while 等)中,JavaScript 引擎应该做什么,就像这样:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
而且由于它们是在运行分步代码之前处理的,因此很难知道当它们处于控制结构中时要做什么。
虽然直到 ES2015 才指定这样做,但它是一个允许的扩展,以支持块中的函数声明。不幸的是(并且不可避免地),不同的引擎做了不同的事情。
从 ES2015 开始,规范说明了要做什么。事实上,它提供了三件不同的事情要做:
- 如果在网络浏览器上不处于松散模式,JavaScript 引擎应该做一件事
- 如果在网络浏览器上处于松散模式,JavaScript 引擎应该执行其他操作
- 如果在 strict 模式下(浏览器与否),JavaScript 引擎应该做另一件事
松散模式的规则很棘手,但在 strict 模式下,块中的函数声明很简单:它们是块的本地(它们具有 块范围 ,这在 ES2015 中也是新的),并且它们被提升到块的顶部。所以:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
“匿名”function 表达式
第二种常用形式称为匿名函数表达式:
var y = function () {
console.log('y');
};
与所有表达式一样,它会在代码的逐步执行过程中到达时进行评估。
在 ES5 中,此创建的函数没有名称(它是匿名的)。在 ES2015 中,如果可能,函数会通过从上下文中推断来分配一个名称。在上面的示例中,名称将是 y。当函数是属性初始值设定项的值时,会执行类似的操作。 (有关何时发生这种情况和规则的详细信息,请在 the specification 中搜索 SetFunctionName——它到处出现。)
命名为function 表达式
第三种形式是命名函数表达式(“NFE”):
var z = function w() {
console.log('zw')
};
此创建的函数有一个专有名称(在本例中为w)。与所有表达式一样,在逐步执行代码时会评估 this。函数名不加到表达式出现的作用域;名称 在函数本身的范围内:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
请注意,NFE 经常成为 JavaScript 实现的错误来源。例如,IE8 和更早版本处理 NFE completely incorrectly,在两个不同的时间创建两个不同的函数。 Safari 的早期版本也存在问题。好消息是当前版本的浏览器(IE9 及更高版本,当前 Safari)不再存在这些问题。 (但遗憾的是,在撰写本文时,IE8 仍在广泛使用,因此将 NFE 与 Web 代码一起使用通常仍然存在问题。)
访问函数初始化器 (ES5+)
有时功能可能会在很大程度上被忽视; 访问器函数就是这种情况。这是一个例子:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
注意,我使用函数的时候,没有使用()!那是因为它是一个属性的访问器函数。我们以正常方式获取和设置属性,但在后台调用函数。
您还可以使用Object.defineProperty、Object.defineProperties 和Object.create 的鲜为人知的第二个参数创建访问器函数。
箭头函数表达式(ES2015+)
ES2015 为我们带来了箭头函数。这是一个例子:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
看到n => n * 2 隐藏在map() 调用中的东西了吗?这是一个函数。
关于箭头函数的几点说明:
-
他们没有自己的this。相反,它们关闭定义它们的上下文的this。 (它们还关闭了arguments 和super。)这意味着它们中的this 与创建它们的this 相同,并且不能更改。
正如您在上面看到的那样,您不使用关键字function;相反,您使用=>。
上面的n => n * 2 示例就是其中的一种形式。如果您有多个参数来传递函数,则使用括号:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(请记住,Array#map 将条目作为第一个参数传递,索引作为第二个参数传递。)
在这两种情况下,函数体只是一个表达式;函数的返回值将自动成为该表达式的结果(您不使用显式的return)。
如果您做的不仅仅是单个表达式,请照常使用{} 和显式return(如果您需要返回值):
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
没有{ ... } 的版本称为带有表达式主体 或简洁主体 的箭头函数。 (另外:简洁箭头函数。)带有{ ... } 定义主体的箭头函数是带有函数主体的箭头函数。 (另外:一个详细箭头函数。)
对象初始化器中的方法声明(ES2015+)
ES2015 允许以更短的形式声明引用称为方法定义的函数的属性;它看起来像这样:
var o = {
foo() {
}
};
在 ES5 及更早版本中几乎等效的是:
var o = {
foo: function foo() {
}
};
区别(除了冗长)是方法可以使用super,但函数不能。因此,例如,如果您有一个使用方法语法定义(例如)valueOf 的对象,它可以使用super.valueOf() 来获取 Object.prototype.valueOf 将返回的值(在可能用它做其他事情之前),而 ES5版本必须改为Object.prototype.valueOf.call(this)。
这也意味着该方法具有对其定义的对象的引用,因此如果该对象是临时对象(例如,您将其作为源对象之一传递给 Object.assign),方法语法 可能意味着对象被保留在内存中,否则它可能会被垃圾回收(如果 JavaScript 引擎没有检测到这种情况并在没有任何方法使用 super 的情况下处理它)。
class (ES2015+) 中的构造函数和方法声明
ES2015 为我们带来了class 语法,包括声明的构造函数和方法:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
上面有两种函数声明:一种用于构造函数,其名称为Person,另一种用于getFullName,它是分配给Person.prototype的函数。