Javascript: Augment a standard class without global prototype changes

43 Views Asked by At

A classic "bad idea" in JS is to alter the prototypes of inbuilt types:

Array.prototype.last = function() { return this[this.length - 1] }; 
[1,2,3].last() // 3

In ES6 we can extend the inbuilt Array type:

class MyArray extends Array {
  last() {
    return this[this.length - 1];
  }
}

But there's no great way to add these functions without copying:

a = [1, 2, 3]
b = new MyArray(...a);
// [1, 2, 3]
b === a 
// False

We can update the object's prototype:

a = [1, 2, 3]
Object.setPrototypeOf(a, MyArray.prototype);
a.last() // 3
Object.setPrototypeOf(a, Array.prototype);
a.last() // TypeError

However, MDN says:

Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, currently a very slow operation in every browser and JavaScript engine.

Helper functions or container classes can help, but their syntax is unwieldy:

ArrayHelper.last(a); // 3
MyArrayWrapper(a).last(); // 3
MyArrayWrapper.get(0); // Ugh, we lose []-based indexing.

The ideal solution would be something like:

a = [1, 2, 3];
b = asMyArray(a);
b.last(); // 3
a.last(); // TypeError

// But avoid doing a copy, and have both objects reference the same memory:
b[0] = 10; 
a[0] == 10; // true

or, if it's easier

a = [1, 2, 3];
applyMyArrayPrototype(a);
a.last(); // 3
a.dropMyArrayPrototype();
a.last(); // TypeError

Is there a way to achieve this in JS?

0

There are 0 best solutions below