Javascript 如何从方法中创建与当前对象具有相同类的新对象

Javascript 如何从方法中创建与当前对象具有相同类的新对象,javascript,class,inheritance,new-operator,Javascript,Class,Inheritance,New Operator,我有一个数组的子类,用于内部项目。我添加的一些方法需要返回一个新数组。我正试图找出创建新阵列的最佳方法 我不想硬编码它来创建我的特定子类的数组,因为如果其他人对我的类进行子类化,这就是源数组类,那么它应该创建该子类的对象。换句话说,我想创建一个新对象,它与当前的这个对象是同一个类,而不管在我下面还有多少其他子类 Array类本身已经做了类似的事情。如果子类数组,然后对子类的实例使用普通的.map()函数,它将使用您的类返回一个新数组 这个和那个.map()使用的ECMAScript规范是相同的,

我有一个数组的子类,用于内部项目。我添加的一些方法需要返回一个新数组。我正试图找出创建新阵列的最佳方法

我不想硬编码它来创建我的特定子类的数组,因为如果其他人对我的类进行子类化,这就是源数组类,那么它应该创建该子类的对象。换句话说,我想创建一个新对象,它与当前的
这个
对象是同一个类,而不管在我下面还有多少其他子类

Array类本身已经做了类似的事情。如果子类数组,然后对子类的实例使用普通的
.map()
函数,它将使用您的类返回一个新数组

这个和那个
.map()
使用的ECMAScript规范是相同的,但我无法理解所有这些规范逻辑在实际的Javascript代码中转换成了什么

现在,我正在使用:

let newArray = new this.constructor();
它似乎在我自己的小世界中起作用,但我想知道
ArraySpeciesCreate()
中的所有逻辑是否应该包含更多的代码

仅供参考,以下是ECMAScript规范中的
ArraySpeciesCreate()
,它应该遵循
.map()
来创建它返回的新数组。这大概也是我想要遵循的

在您自己的类中,将使用什么实际的Javascript代码来实现这一点


下面是我的数组子类中的一个方法示例:

// break an array up into chunks
// the last chunk may have fewer items
// returns an array of arrays
chunk(chunkSize) {
    if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
        throw new TypeError('chunkSize must be a positive integer');
    }
    const output = new this.constructor();
    const numChunks = Math.ceil(this.length / chunkSize);
    let index = 0;
    for (let i = 0; i < numChunks; i++) {
        output.push(this.slice(index, index + chunkSize));
        index += chunkSize;
    }
    return output;
}

是我要问的应该实现
ArraySpeciesCreate()
逻辑的那一个。

我仍然认为不应该将
Array
子类化,但我可以展示
ArraySpeciesCreate
在ECMAScript中实现时的外观:

if (!Array.isArray(this))               // if not called on an array
    return new Array(len);

const {constructor} = this;
if (typeof constructor == "function"    // if the constructor looks like a constructor,
  && !Function.prototype.isPrototypeOf(constructor) // but appears to stem from a
                                        // different realm (iframe etc)
  && constructor.name == "Array")       // and might be that realm's builtin Array
    return new Array(len);

const C = constructor?.[Symbol.species];
if (C == null)                          // if there's no subclass species
    return new Array(len);

return new C(len);
您可能可以忽略测试跨领域实例这一奇怪的边缘案例,它实际上并不完全有效。(我怀疑是否有一个好的方法来检查这些,而且似乎不可能重现GetFunctionRealm步骤-尽管您可能希望将
构造函数作为本机函数加入其中)

通常,它只需要访问
this.constructor
,并使用该构造函数的结果而不是当前类来构造新实例

或者,您可以作弊并使用
Array.prototype.slice.call(this,0,0)
:-)


另一个很好的解决方案是es抽象库,它尝试实现尽可能精确的抽象操作。

我仍然认为不应该将
数组
子类化,但我可以展示
数组规范创建
在ECMAScript中实现时的外观:

if (!Array.isArray(this))               // if not called on an array
    return new Array(len);

const {constructor} = this;
if (typeof constructor == "function"    // if the constructor looks like a constructor,
  && !Function.prototype.isPrototypeOf(constructor) // but appears to stem from a
                                        // different realm (iframe etc)
  && constructor.name == "Array")       // and might be that realm's builtin Array
    return new Array(len);

const C = constructor?.[Symbol.species];
if (C == null)                          // if there's no subclass species
    return new Array(len);

return new C(len);
您可能可以忽略测试跨领域实例这一奇怪的边缘案例,它实际上并不完全有效。(我怀疑是否有一个好的方法来检查这些,而且似乎不可能重现GetFunctionRealm步骤-尽管您可能希望将
构造函数作为本机函数加入其中)

通常,它只需要访问
this.constructor
,并使用该构造函数的结果而不是当前类来构造新实例

或者,您可以作弊并使用
Array.prototype.slice.call(this,0,0)
:-)


另一个很好的解决方案是es抽象库,它试图实现尽可能精确的抽象操作。

这里要感谢Bergi解释ECMAScript逻辑在实际Javascript代码中的含义

为了完整起见,我想共享一个通用实用函数,我正在使用它创建另一个对象,就像我已经拥有的对象一样(即使它是一个派生类,我不知道),它不仅仅是特定于数组的。由于任何子类都可能希望使用这种类型的逻辑,因此我们想知道为什么Javascript中没有内置这种逻辑

// create a new object using the class of an existing object (which might be a derived class)
// by following the ECMAScript procedure except for the realm detection part

function isConstructor(f) {
    return typeof f === "function" && !!f.prototype;
}

function speciesCreate(originalObject, fallbackConstructor, ...args) {
    const {constructor} = originalObject;
    if (constructor) {
        const C = constructor[Symbol.species];
        if (isConstructor(C)) {
            return new C(...args);
        } else if (isConstructor(constructor)) {
            return new constructor(...args);
        }
    }
    return new fallbackConstructor(...args);
}
因此,在我的
ArrayEx
类中,不要在方法内部使用此方法来创建与当前实例相同类的新对象:

let newObj = new this.constructor();
我会用这个:

let newObj = speciesCreate(this, ArrayEx);
并且,您可以在任何特定情况下向构造函数传递参数(如果需要)



我看到这个逻辑的一个问题是,如果派生类重写了Symbol.species,并将其设置为某个基类,但我打算创建一个至少具有我的类功能的新对象,那么派生类将阻止它。我想就是这样。如果一个派生类这样做破坏了东西,我想他们会处理破坏的后果。

这里要感谢Bergi解释ECMAScript逻辑在实际Javascript代码中的含义

为了完整起见,我想共享一个通用实用函数,我正在使用它创建另一个对象,就像我已经拥有的对象一样(即使它是一个派生类,我不知道),它不仅仅是特定于数组的。由于任何子类都可能希望使用这种类型的逻辑,因此我们想知道为什么Javascript中没有内置这种逻辑

// create a new object using the class of an existing object (which might be a derived class)
// by following the ECMAScript procedure except for the realm detection part

function isConstructor(f) {
    return typeof f === "function" && !!f.prototype;
}

function speciesCreate(originalObject, fallbackConstructor, ...args) {
    const {constructor} = originalObject;
    if (constructor) {
        const C = constructor[Symbol.species];
        if (isConstructor(C)) {
            return new C(...args);
        } else if (isConstructor(constructor)) {
            return new constructor(...args);
        }
    }
    return new fallbackConstructor(...args);
}
因此,在我的
ArrayEx
类中,不要在方法内部使用此方法来创建与当前实例相同类的新对象:

let newObj = new this.constructor();
我会用这个:

let newObj = speciesCreate(this, ArrayEx);
并且,您可以在任何特定情况下向构造函数传递参数(如果需要)



我看到这个逻辑的一个问题是,如果派生类重写了Symbol.species,并将其设置为某个基类,但我打算创建一个至少具有我的类功能的新对象,那么派生类将阻止它。我想就是这样。若一个派生类这样做破坏了事物,我想他们会处理破坏的后果。

。。。只是出于好奇。。。这部作品已经被偶然发现了吗?@PeterSeliger-no,不知道这一点,也不知道如何使用i