12.1 ECMAScript 6 中的可调用实体
在 ES6 中,有如下可调用的实体:
- 传统的函数(通过函数表达式和函数声明创建)
- 生成器函数(通过生成器函数表达式和生成器函数声明创建)
- 箭头函数(只有一种表达式的形式)
- 方法(通过对象字面量和类定义中的方法定义创建)
- 生成器方法(通过对象字面量和类类定义的生成器方法定义创建)
- 类(通过类表达式和类声明创建)
注意我区分了:
- 这种实体:例如传统函数
- 创建实体的语法:例如函数表达式和函数声明
尽管它们的表现差异很大(后面讲解),但是所有实体都是函数。例如:
> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'
接下来,我们详细学习每一种可调用实体。
12.1.1 ES6 中的调用方式
某些调用可以发生在任何地方,其它调用被限制在指定的区域。
12.1.1.1 可以在任何地方发生的调用
在 ES6 中有三种调用可以发生在任何地方:
- 函数调用:
func(3, 1)
- 方法调用:
obj.method('abc')
- 构造器调用:
new Constr(8)
对于函数调用,记住这点很重要:大多数 ES6 代码会被包含进模块,并且模块体隐式地设为了严格模式。
12.1.1.2 通过 super
调用的方式被限制在指定的区域
两种调用可以通过 super
关键字发生;它们的使用被限制在指定的区域:
父方法调用:
super.method('abc')
仅在对象字面量或者继承类定义的方法定义中可用。
父构造器调用:
super(8)
仅在继承类特殊的方法
constructor()
中可用。
12.1.1.3 非方法函数( non-method function )和方法
非方法函数和方法之间的区别在 ES6 中变得更加明显了。现在两者都有特殊的实体,并且只有它们能做相应的事情:
- 箭头函数就是非方法函数。它从父作用域中拉取
this
和其它变量(“词法的this
”)。 - 方法定义就是方法。它支持
super
,指向父属性和实现父方法调用。
12.1.2 传统的函数
有些函数来自于 ES5 。有两种方式创建这些函数:
- 函数表达式:
const foo = function (x) { ··· };
- 函数声明:
function foo(x) { ··· }
this
规则:
- 函数调用:在严格模式下,
this
为undefined
,宽松模式下为全局对象。 - 方法调用:
this
是方法调用消息的接收者(或者是call/apply
的第一个参数)。 - 构造器调用:
this
是新创建的实例。
12.1.3 生成器函数
生成器函数在生成器章节介绍。它们的语法和传统的函数类似,但是有一个额外的星号:
- 生成器函数表达式:
const foo = function* (x) { ··· };
- 函数声明:
function* foo(x) { ··· }
this
规则如下所示。注意 this
绝不会指向当前生成器对象。
- 函数/方法调用:和传统的函数一样处理
this
。该调用的返回值是生成器对象。 - 构造器调用:在一个生成器函数中访问
this
会引起ReferenceError
。构造器调用的结果是一个生成器对象。
12.1.4 方法定义
方法定义在对象字面量中出现:
let obj = {
add(x, y) {
return x + y;
}, // comma is required
sub(x, y) {
return x - y;
}, // comma is optional
};
在类定义中:
class AddSub {
add(x, y) {
return x + y;
} // no comma
sub(x, y) {
return x - y;
} // no comma
}
方法定义是唯一一个可以使用 super
指向父属性的地方。Only method definitions that use super produce functions that have the property [[HomeObject]], which is required for that feature (details are explained in the chapter on classes).
规则:
- 函数调用:如果取出一个方法,像函数一样调用它,这个方法就表现得和传统函数一样了。
- 方法调用:和传统函数一样,但是额外地允许你使用
super
。 - 构造器调用:产生一个
TypeError
。
在类定义中,名为 constructor
的方法很特别,在后面会有解释。
12.1.5 生成器方法定义
生成器方法在生成器章节介绍。它的语法和函数定义类似,但是带有一个额外的星号:
let obj = {
* generatorMethod(···) {
···
},
};
class MyClass {
* generatorMethod(···) {
···
}
}
规则:
- 调用一个生成器方法返回一个生成器对象。
- 你可以使用
this
和super
,就像你在通常的方法定义中一样。
12.1.6 箭头函数
箭头函数在它自己的那一章讲解:
let squares = [1,2,3].map(x => x * x);
下面的变量词法上在箭头函数里(从父作用域中拉取):
arguments
super
this
new.target
规则:
- 函数调用:词法的
this
等等。 - 方法调用:你可以使用箭头函数作为方法,但是
this
仍然是词法的,并不指向方法调用消息的接收者。 - 构造函数调用:产生一个
TypeError
。
12.1.7 类
类在它自己的那一章中讲解。
// Base class: no `extends`
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
// This class is derived from `Point`
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color;
}
}
constructor
方法很特殊,因为它“变成了”类。也就是说,类和构造器函数非常相似:
> Point.prototype.constructor === Point
true
规则:
- 函数/方法调用:类不能以函数或者方法的形式调用(在类的那一章中解释了为什么)。
- 构造器调用:遵循一个支持子类的协议。在基类中,创建一个实例,然后
this
指向它。一个继承类从它的父类中获取它的实例,这就是为什么要在访问this
之前使用super
。