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