Starting from an object literal {}
or new Object()
, is there any way to modify the instance such that it behaves like an Array exotic object?
Special behaviours of Array exotics:
const a = [];
console.log(a instanceof Array); // true
console.log(a.__proto__ === Array.prototype); // true
console.log(a.length); // 0
a.push(true);
console.log(a.length); // length increases to 1
console.log(Object.hasOwnProperty(a, 'length')); // false
console.log('length' in a); // true
a[1] = true;
console.log(a.length); // length increases to 2 to fit element [1]
console.log(a); // visualized as Array [ true, true ]
console.log(JSON.stringify(a)); // serialized as "[true,true]"
a.length = 0;
console.log(a[1]); // element [1] removed because length decreased to 0
We can achieve some Array behaviours by prototypically inheriting from Array
:
const o = {};
Object.setPrototypeOf(o, Array.prototype);
console.log(o instanceof Array); // true
console.log(o.__proto__ === Array.prototype); // true
console.log(o.length); // 0
o.push(true);
console.log(o.length); // length increases to 1
console.log(Object.hasOwnProperty(o, 'length')); // false
console.log('length' in o); // true
o[1] = true;
console.log(o.length); // length remains at 1
console.log(o); // visualized as [true, 1: true]
console.log(JSON.stringify(o)); // serialized as {"0":true,"1":true,"length":1}
o.length = 0;
console.log(o[1]); // element [1] remains despite length increase
Clearly, Object.setPrototypeOf()
gives us some of the functionality of Arrays, but not all its invariants are maintained and it's logged and serialized differently. Are there further tweaks we can do to our object instance to make it behave even more like an Array exotic object?
Creating an instance of a class which extends Array
gives much better results:
const c = new class extends Array {};
console.log(c instanceof Array); // true
console.log(c.__proto__ === Array.prototype); // false
console.log(c.__proto__.__proto__ === Array.prototype); // true
console.log(c.length); // 0
c.push(true);
console.log(c.length); // length increases to 1
console.log(Object.hasOwnProperty(c, 'length')); // false
console.log('length' in c); // true
c[1] = true;
console.log(c.length); // length increases to 2 to fit element [1]
console.log(c); // visualized as Array [ true, true ]
console.log(JSON.stringify(c)); // serialized as "[true,true]"
c.length = 0;
console.log(c[1]); // element [1] removed because length decreased to 0
Is there something different about how prototypical inheritance works via extends
versus via setPrototypeOf
? Are there effects that we can apply to an Object instance in addition to setPrototypeOf
to get similar results to what we've achieved with extends
?