使用ES6类扩展数组

23

我听说ES6现在最终允许对数组进行子类化。这里是一个示例,由以下人员提供:

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


2
我认为Traceur没有正确实现内置子类化(无论如何,没有内部支持很难做到正确)。 - Qantas 94 Heavy
我相信Traceur可以正确地实现子类化。即使在ES6中,它只是对TypeScript、Traceur和其他转译器中使用的常见模式进行了语法糖处理。 - Jack Wester
这对我没有用。 - Dimitri Kopriwa
2个回答

17

长答案

在正常情况下,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中,子类化应该可以工作。如果他们没有使用新的class extends语法糖来处理此过程,您可能需要手动使用__proto__技巧。您将无法使用转换器(如Traceur和TypeScript)在ES5中完成此操作,但是您可能能够使用上面的代码在ES5中使用Firefox进行操作(据我所知,Firefox已经支持__proto__了相当长的时间)。


4
MDN中得知:虽然大多数浏览器今天都支持Object.prototype.__proto__,但它的存在和确切行为只在ECMAScript 6规范中作为遗留特性进行了标准化,以确保与Web浏览器的兼容性。最好使用Object.setPrototypeOf()Reflect.setPrototypeOf - Oriol

1
问题在于您重写了构造函数。如果删除您的constructor() { super(); },则您的示例将完美运行,包括s.length = 0截断数组。

2
我几乎无法相信这个。如果没有显式的 constructor,你只会得到默认的 constructor(...args) { super(...args); } - Bergi
在Node.js 4.1+中删除构造函数对我有用,谢谢! - Steven de Salas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接