我正在寻找任何迹象表明“超类化”内置类型是否有效根据规格。也就是说,给定任何假设的 ECMAScript 一致实现,“超类化”内置函数是否会通过影响类构造函数的创建算法来破坏运行时?
“超一流”,我创造的一个术语,指的是一个类,其通过构造它或将其作为函数调用(如果适用)返回的对象将使用相同的内部插槽([[Prototype]] 除外)创建,无论其是什么直接超类是,只要类构造函数的初始[[Prototype]]和类原型在重新分配后仍然位于各自的继承链中。因此,为了成为“超类”,一个类不得致电 super()
在创作过程中。
当“超类化”一个Array
,我希望它看起来像这样:
// clearly this would break Array if the specification allowed an implementation
// to invoke super() internally in the Array constructor
class Enumerable {
constructor (iterator = function * () {}) {
this[Symbol.iterator] = iterator
}
asEnumerable() {
return new Enumerable(this[Symbol.iterator].bind(this))
}
}
function setSuperclassOf (Class, Superclass) {
/* These conditions must be satisfied in order to
* superclass Class with Superclass
*/
if (
!(Superclass.prototype instanceof Object.getPrototypeOf(Class.prototype).constructor) ||
!(Superclass instanceof Object.getPrototypeOf(Class).constructor) ||
(Superclass.prototype instanceof Class)
) {
throw new TypeError(`${Class.name} cannot have their superclass set to ${Superclass.name}`)
}
// Now we can superclass Class with Superclass
Object.setPrototypeOf(Class.prototype, Superclass.prototype)
Object.setPrototypeOf(Class, Superclass)
}
setSuperclassOf(Array, Enumerable)
const array = new Array(...'abc')
// Checking that Array is not broken by Enumerable
console.log(array[Symbol.iterator] === Array.prototype[Symbol.iterator])
// Checking that Enumerable works as expected
const enumerable = array.asEnumerable()
console.log(array instanceof Enumerable)
console.log(!(enumerable instanceof Array))
for (const letter of enumerable) {
console.log(letter)
}
我最大的担忧之一是,在内部,在可能一致的实施中,Array
could可能看起来像这样,这意味着Array
is not“超类”:
class HypotheticalArray extends Object {
constructor (...values) {
const [value] = values
// this reference would be modified by superclassing HypotheticalArray
super()
if (values.length === 1) {
if (typeof value === 'number') {
if (value !== Math.floor(value) || value < 0) {
throw new RangeError('Invalid array length')
}
this.length = value
return
}
}
this.length = values.length
for (let i = 0; i < values.length; i++) {
this[i] = values[i]
}
}
* [Symbol.iterator] () {
const { length } = this
for (let i = 0; i < length; i++) {
yield this[i]
}
}
}
// Array constructor actually inherits from Function prototype, not Object constructor
Object.setPrototypeOf(HypotheticalArray, Object.getPrototypeOf(Function))
class Enumerable {
constructor (iterator = function * () {}) {
this[Symbol.iterator] = iterator
}
asEnumerable() {
return new Enumerable(this[Symbol.iterator].bind(this))
}
}
function setSuperclassOf (Class, Superclass) {
/* These conditions must be satisfied in order to
* superclass Class with Superclass
*/
if (
!(Superclass.prototype instanceof Object.getPrototypeOf(Class.prototype).constructor) ||
!(Superclass instanceof Object.getPrototypeOf(Class).constructor) ||
(Superclass.prototype instanceof Class)
) {
throw new TypeError(`${Class.name} cannot have their superclass set to ${Superclass.name}`)
}
// Now we can superclass Class with Superclass
Object.setPrototypeOf(Class.prototype, Superclass.prototype)
Object.setPrototypeOf(Class, Superclass)
}
setSuperclassOf(HypotheticalArray, Enumerable)
const array = new HypotheticalArray(...'abc')
// Array is broken by Enumerable
console.log(array[Symbol.iterator] === HypotheticalArray.prototype[Symbol.iterator])
// Checking if Enumerable works as expected
const enumerable = array.asEnumerable()
console.log(array instanceof Enumerable)
console.log(!(enumerable instanceof HypotheticalArray))
// Iteration does not work as expected
for (const letter of enumerable) {
console.log(letter)
}
然而,Array
is如果需要一致的实现,则为“超类”not打电话super()
:
class HypotheticalArray {
constructor (...values) {
const [value] = values
// doesn't ever invoke the superclass constructor
// super()
if (values.length === 1) {
if (typeof value === 'number') {
if (value !== Math.floor(value) || value < 0) {
throw new RangeError('Invalid array length')
}
this.length = value
return
}
}
this.length = values.length
for (let i = 0; i < values.length; i++) {
this[i] = values[i]
}
}
* [Symbol.iterator] () {
const { length } = this
for (let i = 0; i < length; i++) {
yield this[i]
}
}
}
class Enumerable {
constructor (iterator = function * () {}) {
this[Symbol.iterator] = iterator
}
asEnumerable() {
return new Enumerable(this[Symbol.iterator].bind(this))
}
}
function setSuperclassOf (Class, Superclass) {
/* These conditions must be satisfied in order to
* superclass Class with Superclass
*/
if (
!(Superclass.prototype instanceof Object.getPrototypeOf(Class.prototype).constructor) ||
!(Superclass instanceof Object.getPrototypeOf(Class).constructor) ||
(Superclass.prototype instanceof Class)
) {
throw new TypeError(`${Class.name} cannot have their superclass set to ${Superclass.name}`)
}
// Now we can superclass Class with Superclass
Object.setPrototypeOf(Class.prototype, Superclass.prototype)
Object.setPrototypeOf(Class, Superclass)
}
setSuperclassOf(HypotheticalArray, Enumerable)
const array = new HypotheticalArray(...'abc')
// Array is not broken by Enumerable
console.log(array[Symbol.iterator] === HypotheticalArray.prototype[Symbol.iterator])
// Checking if Enumerable works as expected
const enumerable = array.asEnumerable()
console.log(array instanceof Enumerable)
console.log(!(enumerable instanceof HypotheticalArray))
// Iteration works as expected
for (const letter of enumerable) {
console.log(letter)
}
考虑到这一点,我想引用当前草案中的几点,ECMAScript 2018 https://www.ecma-international.org/ecma-262/:
§22.1.1 数组构造函数 https://www.ecma-international.org/ecma-262/#sec-array-constructor
数组构造函数:
-
创建并初始化一个新的 Array 奇异对象当作为构造函数调用时。
- 被设计为可子类化。它可以用作类定义的扩展子句的值。想要继承异常 Array 行为的子类构造函数必须包含对 Array 构造函数的 super 调用,以初始化作为 Array 异常对象的子类实例。
§22.1.3 数组原型对象的属性 https://www.ecma-international.org/ecma-262/#sec-properties-of-the-array-prototype-object
Array 原型对象有一个 [[Prototype]] 内部槽,其值为内部对象 %ObjectPrototype%。
Array 原型对象被指定为 Array 奇异对象确保与 ECMAScript 2015 规范之前创建的 ECMAScript 代码兼容。
(emphasis added)
我的理解是,一致的实现是not需要内部调用super()
内Array
构造函数以便将实例正确初始化为异国情调的数组,也不需要Object
成为的直接超类Array
(尽管我对第 22.1.3 节的第一段引用似乎确实暗示了这一点)。
我的问题是,上面的第一个片段是否按照规范工作,或者它只是因为当前现有的实现允许才工作?即是第一个的实施HypotheticalArray
不合格?
对于全额赏金奖励,我还想将这个问题应用于String
, Set
, Map
, and TypedArray
(我的意思是Object.getPrototypeOf(Uint8Array.prototype).constructor
).
我将奖励第一个回答的 500 奖励积分严格地解决了我关于 ECMAScript 2015 及更高版本中上述内置函数“超类化”实践的问题(草案中Object.setPrototypeOf()
被介绍)。
我不打算支持 ECMAScript 版本 5.1 及以下版本,因为只能通过访问来修改内置的继承链__proto__
,即not部分anyECMAScript 规范因此依赖于实现。
P.S. I am fully aware of the reasons that practices like this are discouraged, which is why I would like to determine if the specification allows for "superclassing" without "breaking the web", as TC39 likes to say.