为什么在 javascript 中过滤扩展数组的 class 的对象调用它的构造函数?
Why in javascript filtering an object of a class that extends an Array calls it's constructor?
我有这个示例代码:
class TestClass extends Array {
constructor() {
console.log( 'constructor' );
let ar = [];
ar.push( { id: 1, name: 'a' } );
ar.push( { id: 2, name: 'b' } );
ar.push( { id: 3, name: 'c' } );
ar.push( { id: 4, name: 'd' } );
// finalizing object
super( ...ar );
}
Foo() {
console.log( 'foo' );
return this.filter( item => item.id > 2 );
}
}
let t = new TestClass();
console.log( t.Foo() );
这是我已经写过的更简单的版本。我的应用程序一直运行到现在,但在我需要过滤扩展数组中的数据时停止了。
我发现,问题是在我的 class 的对象上调用过滤器函数会在内部调用构造函数。上面的代码显示了该示例。
有没有办法绕过这个问题,因为此时我不能再次调用构造函数。此外,我发现(使用这个简单的 TestClass
)实际输出不是我所期望的——我得到一个包含 4 个项目的数组,id 为 3、4、3、4。谁能解释这里发生了什么?
Symbol.species
提供了一种方法 return 不是派生的 class 的另一个实例,而是用于.e.g.在这种情况下,又是一个 Array
实例。
class TestClass extends Array {
constructor() {
console.log( 'constructor' );
let ar = [];
ar.push( { id: 1, name: 'a' } );
ar.push( { id: 2, name: 'b' } );
ar.push( { id: 3, name: 'c' } );
ar.push( { id: 4, name: 'd' } );
// finalizing object
super( ...ar );
}
static get [Symbol.species]() { return Array; }
Foo() {
console.log( 'foo' );
return this.filter( item => item.id > 2 );
}
}
let t = new TestClass();
let a = t.Foo();
console.log('a : ', a);
console.log('(a instanceof Array) ? ', (a instanceof Array));
console.log('(a instanceof TestClass) ? ', (a instanceof TestClass));
.as-console-wrapper { max-height: 100%!important; top: 0; }
根据规范,
- Let A be ? ArraySpeciesCreate(O, 0).
(这里,O
是原数组,A
是结果)
- Let C be ? Get(originalArray, "constructor").
通俗地说,X.filter
创建一个新对象并将 X
的构造函数应用于它。这就是你的构造函数被再次调用的原因。
总的来说,这个设计需要修复。您的 TestClass
扩展了 Array
,现在,如果您在整个应用程序中将 Array
替换为 TestClass
,它的行为是否相同?显然不是,这意味着您的 TestClass
违反了基本的 OOP 原则,即所谓的 LSP,应该重新设计(例如,通过聚合数组而不是扩展数组)。
我有这个示例代码:
class TestClass extends Array {
constructor() {
console.log( 'constructor' );
let ar = [];
ar.push( { id: 1, name: 'a' } );
ar.push( { id: 2, name: 'b' } );
ar.push( { id: 3, name: 'c' } );
ar.push( { id: 4, name: 'd' } );
// finalizing object
super( ...ar );
}
Foo() {
console.log( 'foo' );
return this.filter( item => item.id > 2 );
}
}
let t = new TestClass();
console.log( t.Foo() );
这是我已经写过的更简单的版本。我的应用程序一直运行到现在,但在我需要过滤扩展数组中的数据时停止了。
我发现,问题是在我的 class 的对象上调用过滤器函数会在内部调用构造函数。上面的代码显示了该示例。
有没有办法绕过这个问题,因为此时我不能再次调用构造函数。此外,我发现(使用这个简单的 TestClass
)实际输出不是我所期望的——我得到一个包含 4 个项目的数组,id 为 3、4、3、4。谁能解释这里发生了什么?
Symbol.species
提供了一种方法 return 不是派生的 class 的另一个实例,而是用于.e.g.在这种情况下,又是一个 Array
实例。
class TestClass extends Array {
constructor() {
console.log( 'constructor' );
let ar = [];
ar.push( { id: 1, name: 'a' } );
ar.push( { id: 2, name: 'b' } );
ar.push( { id: 3, name: 'c' } );
ar.push( { id: 4, name: 'd' } );
// finalizing object
super( ...ar );
}
static get [Symbol.species]() { return Array; }
Foo() {
console.log( 'foo' );
return this.filter( item => item.id > 2 );
}
}
let t = new TestClass();
let a = t.Foo();
console.log('a : ', a);
console.log('(a instanceof Array) ? ', (a instanceof Array));
console.log('(a instanceof TestClass) ? ', (a instanceof TestClass));
.as-console-wrapper { max-height: 100%!important; top: 0; }
根据规范,
- Let A be ? ArraySpeciesCreate(O, 0).
(这里,O
是原数组,A
是结果)
- Let C be ? Get(originalArray, "constructor").
通俗地说,X.filter
创建一个新对象并将 X
的构造函数应用于它。这就是你的构造函数被再次调用的原因。
总的来说,这个设计需要修复。您的 TestClass
扩展了 Array
,现在,如果您在整个应用程序中将 Array
替换为 TestClass
,它的行为是否相同?显然不是,这意味着您的 TestClass
违反了基本的 OOP 原则,即所谓的 LSP,应该重新设计(例如,通过聚合数组而不是扩展数组)。