Array subclassing with setPrototypeOf

前端 未结 2 1809
猫巷女王i
猫巷女王i 2020-12-11 13:44

So I read some blog posts, SO threads and other lectures about subclassing Array in JavaScript. The general view on the topic is that there is no way to create a subclass wi

相关标签:
2条回答
  • 2020-12-11 14:16

    The article discusses how to create an array "subclass". That is, we want to create an object that has Array.prototype in its prototype chain, but with an immediate prototype parent that is not Array.prototype (i.e., so that the prototype parent can provide additional methods beyond the array prototype).

    That article says that a fundamental difficulty in creating an array "subclass" is that arrays get their behavior from both

    1. their prototype, and
    2. simply being array instances.

    If arrays inherited all their behavior from Array.prototype, our work would be very quick. We would simply create an object whose prototype chain includes Array.prototype. That object would make an ideal prototype for our array-subclass instances.

    However, arrays have special automatic behaviors that are unique to array instances, and are not inherited from the prototype. (In particular, I mean the behaviors around the length property automatically changing when the array changes, and vice versa.) These are behaviors supplied to each array instance when it is created by the Array constructor, and there is no way to mimic them faithfully in ECMAScript 5. Therefore, the instance of your array subclass must be originally created by the Array constructor. This is non-negotiable, if we want the appropriate length behaviors.

    This requirement conflicts with our other requirement that the instance must have a prototype that is not Array.prototype. (We don't want to add methods to Array.prototype; we want to add methods to an object that uses Array.prototype as its own prototype .) In ECMAScript 5, any object created using the Array constructor must have the prototype parent of Array.prototype. The ECMAScript 5 spec provides no mechanism to change an object's prototype after it is created.

    By contrast, ECMAScript 6 does provide such a mechanism. Your approach is quite similar to the __proto__-based approach described in the article, under the section "Wrappers. Prototype chain injection.," except you use ECMAScript 6's Object.setPrototypeOf instead of __proto__.

    Your solution correctly satisfies all of the following requirements:

    1. Each instance actually is an array (i.e., has been constructed by the Array constructor). This ensures that the [[Class]] internal property is correct, and length behaves correctly.
    2. Each instance has an immediate prototype that is not Array.prototype, but still includes Array.prototype in its prototype chain.

    These requirements were previously impossible to satisfy in ES5, but ES6 makes it fairly easy. In ES5, you could have an array instance that fails to satisfy requirement #2, or a plain object that fails to satisfy requirement #1.

    0 讨论(0)
  • 2020-12-11 14:23

    Actually array subclassing is possible without even touching Object.setPrototypeOf() or __proto__ by utilizing the mysterious Array.of() method. Array.of() has the ability to switch the constructor function that it uses to construct an array. Since normally it's bound to the Array object it produces normal arrays however once it's bound to another object which can be used as a constructor (a.k.a function) it uses that object as a constructor. Lets make some array sub-classing with Array.of()

    function SubArr(){}
    SubArr.prototype = Object.create(Array.prototype);
    SubArr.prototype.last = function(){return this[this.length-1]};
    
    var what = Array.of.call(SubArr, 1, 2, 3, 4, "this is last");
    console.log(JSON.stringify(what,null,2));
    console.log(what.last());
    console.log(what.map(e => e));
    console.log(what instanceof Array);
    console.log(Array.isArray(what));
    what.unshift("this is first");
    console.log(JSON.stringify(what,null,2));

    So as you see array sub-classing is pretty simple task when done with Array.of() You can find it's specs here. The interesting part is;

    NOTE 2 The of function is an intentionally generic factory method; it does not require that its this value be the Array constructor. Therefore it can be transferred to or inherited by other constructors that may be called with a single numeric argument.

    0 讨论(0)
提交回复
热议问题