15.5 种模式

在 ECMAScript 6 中,另一种使内置的构造器变得可扩展的机制:如果一个方法(比如 Array.prototype.map() 返回一个新实例),应该用什么构造器来创建这个实例呢?

在接下来的小节中会使用如下的辅助函数:

function isObject(value) {
    return (value !== null
       && (typeof value === 'object'
           || typeof value === 'function'));
}

/**
 * Spec-internal operation that determines whether `x` can be used as a constructor.
 */
function isConstructor(x) {
    ···
}

15.5.1 标准的种模式

定制使用方法(比如 Array.prototype.map() )创建的实例的模式被称为种模式:

  • 如果存在 this.constructor[Symbol.species] ,使用它作为新实例的构造器。
  • 否则,使用默认的构造器(例如数组的 Array )。

用 JavaScript 代码实现,这种模式看起来像这样:

function SpeciesConstructor(O, defaultConstructor) {
    let C = O.constructor;
    if (C === undefined) {
        return defaultConstructor;
    }
    if (! isObject(C)) {
        throw new TypeError();
    }
    let S = C[Symbol.species];
    if (S === undefined || S === null) {
        return defaultConstructor;
    }
    if (! isConstructor(S)) {
        throw new TypeError();
    }
    return S;
}

数组的标准种模式在规范是通过 SpeciesConstructor() 实现的。

15.5.2 数组的种模式

下面代码大致描述了种模式是如何应用于数组的:

function ArraySpeciesCreate(originalArray, length) {
    let C = undefined;
    if (Array.isArray(originalArray)) {
        C = originalArray.constructor;
        if (isObject(C)) {
            C = C[Symbol.species];
        }
    }
    if (C === undefined || C === null) {
        return new Array(length);
    }
    if (! IsConstructor(C)) {
        throw new TypeError();
    }
    return new C(length);
}

Array.prototype.map() 返回的数组通过 ArraySpeciesCreate(this, this.length) 创建。

数组种模式在规范中是通过 ArraySpeciesCreate() 操作实现的。

15.5.3 静态方法中的种模式

Promise 中使用了大量的静态方法种模式,例如 Promise.all()

let C = this; // default
if (! isObject(C)) {
    throw new TypeError();
}
// The default can be overridden via the property `C[Symbol.species]`
let S = C[Symbol.species];
if (S !== undefined && S !== null) {
    C = S;
}
if (!IsConstructor(C)) {
    throw new TypeError();
}
let instance = new C(···);

15.5.4 在子类中覆盖默认的 spieces

下面所示是 [Symbol.species] 的默认 getter :

get [Symbol.species]() {
    return this;
}

默认的 getter 在内置的类 ArrayArrayBufferMapPromiseRegExpSet%TypedArray% 中都有实现,并且自动地被这些内置类的子类继承。

有两种方式可以覆盖默认的 species :使用自定义的构造器或者使用 null

15.5.4.1 设置自定义构造器的 species

可以通过静态的 getter (行 A )覆盖默认的 species

class MyArray1 extends Array {
    static get [Symbol.species]() { // (A)
        return Array;
    }
}

这样一来, map() 就返回 Array 的实例了:

let result1 = new MyArray1().map(x => x);
console.log(result1 instanceof Array); // true

如果不覆盖默认的 speciesmap() 就会返回子类的实例:

class MyArray2 extends Array { }

let result2 = new MyArray2().map(x => x);
console.log(result2 instanceof MyArray2); // true

15.5.4.2 通过数据属性指定 species

如果不想使用静态的 getter ,那么就要使用 Object.defineProperty() 。你可以使用赋值,这样会触发 setter ,而这个 setter 并不存在(只有一个 getter )。

例如,这里我们设置 MyArray1speciesArray

Object.defineProperty(
    MyArray1, Symbol.species, {
        value: Array
    });

15.5.4.3 将 species 设置为 null

如果将 species 设置为 null ,就会使用默认的构造器(选用哪个构造器决定于使用哪一个种模式变体,参考前面的小节获取更多相关信息)。

class MyArray3 extends Array {
    static get [Symbol.species]() {
        return null;
    }
}

let result3 = new MyArray3().map(x => x);
console.log(result3 instanceof Array); // true

results matching ""

    No results matching ""