【问题标题】:Extending Array with ES6 classes使用 ES6 类扩展数组
【发布时间】:2014-11-02 14:05:00
【问题描述】:

我听说 ES6 现在终于允许子类化 Array。这是一个由

给出的例子
class Stack extends Array {
    constructor() { super() }
    top() { return this[this.length - 1]; }
  }

  var s = new Stack();
  s.push("world");
  s.push("hello");
  console.log(s.top());  // "hello"
  console.log(s.length); // 2

当然,这行得通。但至少在 Traceur 中,显式设置长度不会截断数组。而通过 console.log 打印时,输出是对象形式而不是数组形式,这表明有人没有将其视为“真实”数组。

这是 Traceur 如何实现子类化内置对象的问题,还是 ES6 的限制?

【问题讨论】:

  • 我认为 Traceur 没有正确实现内置子类化(无论如何,如果没有内部组件,很难正确实现)。
  • 我相信 Traceur 确实正确地实现了子类化。即使在 ES6 中,它也只是在 Typescript、Traceur 和其他转译器中使用的通用模式上加糖。
  • 这对我不起作用

标签: ecmascript-6 traceur


【解决方案1】:

长答案

在正常情况下,Ecmascript 6 中的子类化只是语法糖化,因此它仍然执行 Ecmascript 5 所做的原型链接。这意味着在 Traceur 中扩展类型在大多数情况下与在“真正的”ecmascript 6 中扩展完全相同。

数组实例是特殊的——ECMAScript 6 规范称它们为奇异的。他们对属性长度的处理无法通过普通的 JavaScript 复制。如果你调用你的构造函数,那么会创建一个 Stack 实例,而不是一个奇异对象(exotic 实际上是 ES6 规范中的正式名称)。

但不要绝望,解决方案不是由class extends 加糖本身提供的,而是由__proto__ 属性的(重新)引入提供的。

解决办法

Ecmascript 6 重新引入了可写的 __proto__ 属性。它曾经只在 Firefox 上可用并且已被弃用,但现在在 ES6 中恢复了全部功能。这意味着您可以创建一个真正的数组,然后将其“升级”到您的自定义类。

所以现在您可以执行以下操作:

function Stack(len) {
    var inst = new Array(len);
    inst.__proto__ = Stack.prototype;
    return inst;
}
Stack.prototype = Object.create(Array.prototype);  

简答

所以子类化应该在 ES6 中起作用。如果他们没有设法使用新的class extends 语法和一些尚未公开的技巧来完善该过程,您可能必须手动使用__proto__ 技巧。您将无法在 ES5 中使用诸如 Traceur 和 Typescript 之类的转译器来完成此操作,但您可能可以使用 ES5 中的上述代码使用 Firefox(据我记得)已经支持 __proto__ 相当多的时间时间。

【讨论】:

  • 来自MDN:«虽然Object.prototype.__proto__ 现在在大多数浏览器中都得到支持,但它的存在和确切行为仅在ECMAScript 6 规范中作为遗留功能进行了标准化,以确保与Web 浏览器的兼容性。 »最好使用Object.setPrototypeOf()Reflect.setPrototypeOf
【解决方案2】:

问题是您正在覆盖构造函数。如果您删除 constructor () { super(); },您的示例将完美运行,包括 s.length = 0 截断数组。

【讨论】:

  • 我简直不敢相信。如果没有明确的constructor,您只会得到默认的constructor(...args) { super(...args); }
  • 在 nodejs 4.1+ 中删除构造函数对我有用,谢谢!
猜你喜欢
  • 1970-01-01
  • 2019-07-20
  • 2016-08-20
  • 1970-01-01
  • 2016-07-15
  • 2018-11-28
  • 2020-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多