Array.prototype.slice web-compat issue?

Allen Wirfs-Brock allen at wirfs-brock.com
Tue Sep 10 17:50:49 PDT 2013


On Sep 10, 2013, at 4:38 PM, Andrea Giammarchi wrote:

> right now? the `[[Class]]` such as `'[object Array]'` ... and the magic is this smarter slice.

And what does [[Class]] == "Array" actually tell you? Primarily that the Array length invariant is automatic enforced.  

You seems to be conflating two orthogonal concepts:  
  1) whether an object is an exotic array object (ie, enforces the length invariant)
  2) whether an object inherits from Array.prototype

Even in ES5+web reality, these are separable concepts:

var exotic = [ ]; exotic.__proto__=Object.prototype;
//exotic has the array invariant, Array.isArray(exotic) is true, exotic instanceof Array is false,  exotic does not expose any Array methods

function ArrayLIkeSubclass() {
   this.length = 0;
}
ArrayLikeSubclass.prototype = {
  __proto__: Array.prototype,
  constructor: ArrayLikeSubclass
}

var alike= new ArrayLikeSubclass();
//alike doesn't have array invariant, Array.isArray(alike) is false, alike instanceof Array is true, alike inherits all of the Array methods


function ArrayExoticSubclass() {
   var obj = [ ];
   obj.__proto__ = Object.getPrototypeOf(this);
    return obj;
}
ArrayExoticSubclass.prototype = {
  __proto__: Array.prototype,
  constructor: ArrayExoticSubclass
}

var asub= new ArrayExoticSubclass();
//asub has the array invariant, Array.isArray(asub) is true, asub instanceof Array is true, and it inherits all of the Aarray methods

> 
> I insist this would be way better and already possible today:
> 
> ```javascript
> (function(Object, ArrayPrototype){
>   if (typeof ArrayObject !== 'undefined') return;
>   ArrayObject = function(){};
>   for(var
>     modify = [
>       'concat',
>       'copyWithin',
>       'filter',
>       'map',
>       'slice',
>       'splice'
>     ],
>     keys = Object.getOwnPropertyNames(ArrayPrototype),
>     create = function(method) {
>       return function() {
>         return Object.setPrototypeOf(
>           method.apply(this, arguments),
>           Object.getPrototypeOf(this)
>         );
>       };
>     },
>     i = keys.length,
>     current, key;
>     i--;
>   ) {
>     key = keys[i];
>     current = Object.getOwnPropertyDescriptor(ArrayPrototype, key);
>     if (~modify.indexOf(key)) current.value = create(current.value);
>     Object.defineProperty(ArrayObject.prototype, key, current);
>   }
> }(Object, Array.prototype));
> ```
> 
> where any class that extends `ArrayObject` will have the new behavior and less problem with the past.
> 
> ```javascript
> var a = new ArrayObject;
> a.push(1, 2, 3);
> a.slice() instanceof ArrayObject; // true
> ```

The above is essentially equivalent to the following ES6 code:

class ArrayObject extends Array {
   slice(...args) {return this.constructor.from(super(...args))}
   //do the same thing for concat, map, etc.
}

except that, Array.isArray(new ArrayObject) is true

(new Array).slice() instanceof Array //true
(new Array).slice() instanceof ArrayObject //false
Array.isArray(new Array)  //true
(new ArrayObject).slice instanceof ArrayObject //true
(new ArrayObject).slice instanceof Array //true
Array.isArray(new ArrayObject) //true

All the ES6 spec. does for slice and friends is to make the subclass over-rides unnecessary. You will get the exact same results, without the over-rides.

Allen










More information about the es-discuss mailing list