archives

« Bugzilla Issues Index

#264 — Support for changing the prototype of an object using `Object.setPrototypeOf`


Created attachment 9
Demo of the advantages of being able to explicitly set the prototype of an object.

Browsers support a non-standard deprecated property called `__proto__` on objects. This property points to the prototype of the object, and it's both readable and writable.

Since the `__proto__` property deprecated, JavaScript engines like Rhino don't support it. Instead they support a method `Object.getPrototypeOf` to get the prototype of an object.

However, Rhino doesn't allow the prototype of an object to be changed. To do so I suggest the implementation of a method `Object.setPrototypeOf` which would allow the internal `[[proto]]` property of an object to be changed iff it's not sealed or frozen.

Syntax: `Object.setPrototypeOf(object, prototype)`

Being able to change the internal `[[proto]]` property of an object has many advantages. For example, in some cases it's necessary to return functions from constructors. This is only possible in JavaScript if we use the constructor as a factory. However since the factory returns a function it wouldn't be an instance of the constructor, and wouldn't be able to share methods on its prototype:

Counter.prototype = function () {}; // Allow `instanceof Function` to return `true`, as well as `call`, `apply`, etc to be used.
Counter.prototype.constructor = Counter; // Reset the `constructor` property of the prototype to point to the constructor `Counter`.
Counter.prototype.reset = function () {
while (this() < this.max);
};

var counter = new Counter(3);

alert(counter()); // 1
counter.reset();

alert(counter()); // 1
alert(counter()); // 2
counter.reset();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3

function Counter(max) {
var count = 0;

counter.max = Math.abs(parseInt(max)) || 1;

counter.__proto__ = Counter.prototype; // Setting the internal `[[proto]]` property of the function being returned to the prototype of the constructor.

return counter;

function counter() {
if (count < counter.max) return ++count;
else return count = 1;
}
}

This program will not work on Rhino because on line 6 we are setting `counter.__proto__` to `Counter.prototype`. However since the `__proto__` property is not supported in Rhino, the prototype of `Counter` is never added to the prototype chain. Hence calling `counter.reset` would throw a `TypeError` since we are attempting to invoke `undefined`. To solve this problem we should use `Object.setPrototypeOf`:

Counter.prototype = function () {}; // Allow `instanceof Function` to return `true`, as well as `call`, `apply`, etc to be used.
Counter.prototype.constructor = Counter; // Reset the `constructor` property of the prototype to point to the constructor `Counter`.
Counter.prototype.reset = function () {
while (this() < this.max);
};

var counter = new Counter(3);

print(counter()); // 1
counter.reset();

print(counter()); // 1
print(counter()); // 2
counter.reset();

print(counter()); // 1
print(counter()); // 2
print(counter()); // 3

function Counter(max) {
var count = 0;

counter.max = Math.abs(parseInt(max)) || 1;

Object.setPrototypeOf(counter, Counter.prototype); // Setting the internal `[[proto]]` property of the function being returned to the prototype of the constructor.

return counter;

function counter() {
if (count < counter.max) return ++count;
else return count = 1;
}
}

I sincerely hope this proposal will be taken into consideration. Being able to set the prototype of an object has a lot of uses. Allow programmers all over the world to make the best possible use of this new feature.


http://stackoverflow.com/q/9238737/783743


I can confirm this would be very useful. Another reason to add it is that developers might want to extend a class that's not yet defined. For example:

define B

define A

extend B from A

Which is easily achievable with __proto__


This is also the only way to set up circular prototype chains, e.g. the way the classes Object and Function inherit each other in EcmaScript.


Moved to Harmony Product because this isn't a bug in the ES5/5.1 spec. Intead, this is a request for a new feature.


Another use case is when working with JSON and DB/PubSub libraries that sit on it. People commonly turn around and copy the structure again just to get it into an object with behavior. Simply swapping the initial object's proto would be much faster/cleaner.


Object.setPrototypeOf is included in ES6