Pat*_*rts 15 javascript arrays language-lawyer prototypal-inheritance ecmascript-6
我正在寻找任何指示是否"超类"内置类型将根据规范工作.也就是说,假设任何假设符合ECMAScript的实现,"内置"的"超类"是否通过影响类构造函数的创建算法来破坏运行时?
"Superclassable",我正在创造的一个术语,指的是一个类,通过构造它来返回它,或者将它作为一个函数调用它,如果适用,将使用相同的内部插槽创建([[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)
}Run Code Online (Sandbox Code Playgroud)
我的一个最大的问题是,在内部,在可能的一致实现,Array 也可能是这样,这将意味着Array是不是 "superclassable":
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)
}Run Code Online (Sandbox Code Playgroud)
但是,如果要求一致的实现不要调用,那么它Array 是 "超类"的: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)
}Run Code Online (Sandbox Code Playgroud)
考虑到这一点,我想参考当前草案ECMAScript 2018中的几点:
Array构造函数:
- 在作为构造函数调用时,创建并初始化一个新的Array异物对象.
- 被设计为可子类化.它可以用作类定义的extends子句的值.打算继承奇异数组行为的子类构造函数必须包含对Array构造函数的超级调用,以初始化作为Array外来对象的子类实例.
Array原型对象有一个[[Prototype]]内部插槽,其值是内部对象%ObjectPrototype%.
Array原型对象被指定为Array外来对象,以确保与ECMAScript 2015规范之前创建的ECMAScript代码兼容.
(重点补充)
我的理解是,一个一致实现不以内部调用所需super()的范围内Array,以正确初始化实例作为阵列异国构造,也不需要Object是直接超类Array(虽然我的§22.1.3第一次报价似乎肯定暗示那一点).
我的问题是,上面的第一个片段是否按照规范工作,或者它是否只能工作,因为当前现有的实现允许它?即是第一个HypotheticalArray不符合的实施?
而对于全赏金奖,我也想申请这个问题String,Set,Map,和TypedArray(我的意思Object.getPrototypeOf(Uint8Array.prototype).constructor).
我将为第一个答案奖励500个赏金点,这个答案严格地解决了我在ECMAScript 2015及其中"超级分类"上述内置函数的问题(其中Object.setPrototypeOf()介绍了草案).
我不打算支持ECMAScript 5.1及更低版本,因为修改内置的继承链只能通过访问来实现__proto__,这不是任何 ECMAScript规范的一部分,因此依赖于实现.
PS我完全清楚不鼓励这样做的原因,这就是为什么我想确定规范是否允许"超级分类"而不是"打破网络",正如TC39喜欢说的那样.
在任何 ECMAScript 内置类上调用setSuperclassOf函数不会影响构造函数的行为。
您的HypotheticalArray构造函数不应(不得)调用super()。在规范中,您不仅应该查看给出简短概述的数组构造函数部分,还应该查看\xc2\xa722.1.1.1 Array()、\xc2\xa722.1.1.2 Array(len)小节和\xc2\xa722.1.1.3 Array(...items)给出了调用时发生的详细算法Array(作为函数或构造函数)。他们确实查找newTarget的原型(像往常一样可子类化 - 从 ES6 开始),但他们不查找函数Array本身的原型。相反,它们都直接分派到ArrayCreate算法,该算法仅创建一个对象,设置其原型并安装外来属性语义。
它类似于String(当作为构造函数调用时分派到StringCreate算法)、抽象TypedArray构造函数(它只是抛出并显式声明“ TypedArray 构造函数不会对其执行超级调用。 ”)、具体 TypedArray构造函数(分派给AllocateTypedArray和IntegerIndexedObjectCreate算法),以及Map和Set构造函数(都分派给OrdinaryCreateFromConstructor和ObjectCreate算法)。而且据我所知,所有其他内置构造函数也是一样的,尽管我没有单独检查它们,但 ES8 的内置构造函数太多了。
\n\n\n我的理解是,因为
\nArray.prototype它本身是一个数组外来对象,所以不需要super()在Array构造函数内部调用兼容的实现,以便将实例正确初始化为数组外来对象
不,这与此无关。一个对象不会因为继承自一个外来对象而变得外来。一个对象是奇异的,因为它是专门创建的。的值Array.prototype实际上可以是任何值,它与数组实例的创建无关 - 除此之外它将在new Array调用时用作原型(与 相比new ArraySubclass)。
关于Object.setPrototypeOf(Array.prototype, \xe2\x80\xa6),请注意,它Array.prototype甚至不是像那样的不可变原型外来对象Object.prototype,所以是的,您可以这样做。
| 归档时间: |
|
| 查看次数: |
309 次 |
| 最近记录: |