web-dev-qa-db-ja.com

ES6クラスを使用した配列の拡張

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の制限ですか?

18
user663031

長い答え

通常の場合、Ecmascript 6のサブクラス化は構文上の糖化であるため、Ecmascript5が行う典型的な連鎖を実行します。これは、Traceurで型を拡張することは、ほとんどの場合、「実際の」ecmascript6で拡張することとまったく同じであることを意味します。

配列インスタンスは特別です– ECMAScript6仕様はそれらをエキゾチックと呼んでいます。プロパティの長さの処理は、通常の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);  

短い答え

したがって、サブクラス化should ES6で機能します。新しい__proto__構文を使用してプロセスをシュガー化できず、まだ公開されていないトリックがある場合は、class extendsトリックを手動で使用する必要がある場合があります。 TraceurやTypeScriptなどのトランスパイラーを使用してES5でこれを実現することはできませんが、Firefoxを使用してES5で上記のコードを使用でき、(私が覚えている限り)__proto__をサポートしている可能性があります。かなり長い間。

15
Jack Wester

問題は、コンストラクターをオーバーライドしていることです。 constructor () { super(); }を削除すると、s.length = 0で配列が切り捨てられるなど、例は完全に機能します。

1