代码之家  ›  专栏  ›  技术社区  ›  Patrick Roberts Benjamin Gruenbaum

ecmascript规范是否允许数组“超可分类”?

  •  15
  • Patrick Roberts Benjamin Gruenbaum  · 技术社区  · 6 年前

    我正在寻找任何迹象表明“超激光”内置类型是否有效 根据规范 .也就是说,假设有任何符合ECMAScript的实现,那么“超类化”内置函数是否会通过影响类构造函数的创建算法来中断运行时?

    “超可分类” ,我创造的一个术语,是指一个类,它的对象通过构造它返回,或者如果适用的话将它作为函数调用,将用相同的内部槽来创建(除了[[原型]],不管它的直接超类是什么,只要类const的初始[[原型]]重新分配后,structor和类原型仍在各自的继承链中。因此,为了“超可分类”,一个类 不能打电话 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)
    }

    我最大的担心之一是,在内部,在可能一致的实现中, 数组 能够 可能看起来像这样,这意味着 数组 “超可分类”:

    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)
    }

    然而, 数组 如果需要一致的实现,则为“超可分类” 打电话 超级() 以下内容:

    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 以下内容:

    §22.1.1 The Array Constructor

    数组构造函数:

    • 创建并初始化新的数组外来对象 当作为构造函数调用时。
    • 设计为子类。它可以用作类定义的扩展子句的值。 打算继承外来数组行为的子类构造函数必须包含对数组构造函数的超调用,以初始化作为数组外来对象的子类实例。

    §22.1.3 Properties of the Array Prototype Object

    数组原型对象有一个[[原型]]内部槽,其值为内部对象%object prototype%。

    数组原型对象被指定为数组外部对象 确保与ECMAScript 2015规范之前创建的ECMAScript代码兼容。

    (增加了强调)

    我的理解是一致的实现是 需要内部呼叫 超级() 数组 构造函数以正确地将实例初始化为数组外来的,它也不需要 Object 成为 数组 (尽管我对第22.1.3节的第一句引述显然暗示了这一点)。

    我的问题是,上面的第一个片段是根据规范工作的,还是仅仅因为当前的实现允许它工作而工作的?即第一个 HypotheticalArray 不符合?

    为了获得全额奖金,我还想把这个问题应用到 String ,请 Set ,请 Map ,和 TypedArray (我的意思是 Object.getPrototypeOf(Uint8Array.prototype).constructor )。

    我会奖励500分作为第一个答案 严格地 回答我对2015年及以后ECMAScript(草案 Object.setPrototypeOf() 介绍)。

    我不打算支持ECMAScript 5.1及以下版本,因为只有通过访问 __proto__ ,即 的一部分 任何 ECMAScript规范,因此依赖于实现。

    P.S.我完全了解这种做法不受欢迎的原因,这就是为什么我想确定规范是否允许“超类化”,而不象TC39喜欢说的那样“破坏网络”。