21

我听说 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 的限制?

4

2 回答 2

16

长答案

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

数组实例是特殊的——ECMAScript 6 规范称它们为奇异的。他们对属性长度的处理无法通过普通的 JavaScript 复制。如果您调用构造函数,则会创建一个 Stack 实例,而不是一个外来对象(外来对象实际上是 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 中工作。__proto__如果他们没有设法使用新class extends语法和一些尚未公开的技巧来完善该过程,您可能必须手动使用该技巧。您将无法在 ES5 中使用诸如 Traceur 和 Typescript 之类的转译器来完成此操作,但您可以使用 ES5 中的上述代码(据我记得)已经支持__proto__了很长一段时间的 Firefox。

于 2015-01-29T20:32:22.323 回答
1

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

于 2015-12-08T16:59:01.993 回答