类表达式

类与函数有相似之处,即它们都有两种形式:声明与表达式。函数声明与类声明都以适当的关键词为起始(分别是 functionclass),随后是标识符(即函数名或类名)。函数具有一种表达式形式,无须在 function 后面使用标识符;类似的,类也有不需要标识符的表达式形式。类表达式被设计用于变量声明,或可作为参数传递给函数。

基本的类表达式

此处是与上例中的 PersonClass 等效的类表达式,随后的代码使用了它:

let PersonClass = class {

    // equivalent of the PersonType constructor
    constructor(name) {
        this.name = name;
    }

    // equivalent of PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
};

let person = new PersonClass("Nicholas");
person.sayName();   // outputs "Nicholas"

console.log(person instanceof PersonClass);     // true
console.log(person instanceof Object);          // true

console.log(typeof PersonClass);                    // "function"
console.log(typeof PersonClass.prototype.sayName);  // "function"
javascript

正如此例所示,类表达式不需要在 class 关键字后使用标识符。除了语法差异,类表达式的功能等价于类声明。

使用类声明还是类表达式,主要是代码风格问题。相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,因此对代码运行时的行为影响甚微。

具名类表达式

上一节的示例使用了一个匿名的类表达式,不过就像函数表达式那样,你也可以为类表达式命名。为此需要在 class 关键字后添加标识符,就像这样:

let PersonClass = class PersonClass2 {

    // equivalent of the PersonType constructor
    constructor(name) {
        this.name = name;
    }

    // equivalent of PersonType.prototype.sayName
    sayName() {
        console.log(this.name);
    }
};

console.log(typeof PersonClass);        // "function"
console.log(typeof PersonClass2);       // "undefined"
javascript

此例中的类表达式被命名为 PersonClass2PersonClass2 标识符只在类定义内部存在,因此只能用在类方法内部(例如本例的 sayName() 内)。在类的外部,typeof PersonClass2 的结果为 "undefined",这是因为外部不存在 PersonClass2 绑定。要理解为何如此,请查看未使用类语法的等价声明:

// direct equivalent of PersonClass named class expression
let PersonClass = (function() {

    "use strict";

    const PersonClass2 = function(name) {

        // make sure the function was called with new
        if (typeof new.target === "undefined") {
            throw new Error("Constructor must be called with new.");
        }

        this.name = name;
    }

    Object.defineProperty(PersonClass2.prototype, "sayName", {
        value: function() {

            // make sure the method wasn't called with new
            if (typeof new.target !== "undefined") {
                throw new Error("Method cannot be called with new.");
            }

            console.log(this.name);
        },
        enumerable: false,
        writable: true,
        configurable: true
    });

    return PersonClass2;
}());
javascript

创建具名的类表达式稍微改变了在 JS 引擎内部发生的事。对于类声明来说,外部绑定(用 let 定义)与内部绑定(用 const 定义)有着相同的名称。而类表达式可在内部使用 const 来定义它的不同名称,因此此处的 PersonClass2 只能在类的内部使用。

尽管具名类表达式的行为异于具名函数表达式,但它们之间仍然有许多相似点。二者都能被当作值来使用,这开启了许多可能性,接下来我将会对此进行介绍。