How to Set the Prototype of a JavaScript Object That Has Already Been Instantiated

How to set the prototype of a JavaScript object that has already been instantiated?

Update: ES6 now specifies Object.setPrototypeOf(object, prototype)

.

EDIT Feb. 2012: the answer below is no longer accurate. proto is being added to ECMAScript 6 as "normative optional" which means it isn't required to be implemented but if it is, it must follow the given set of rules. This is currently unresolved but at least it will be officially part of JavaScript's specification.

This question is a lot more complicated than it seems on the surface, and beyond most peoples' pay grade in regards to knowledge of Javascript internals.

The prototype property of an object is used when creating new child objects of that object. Changing it does not reflect in the object itself, rather is reflected when that object is used as a constructor for other objects, and has no use in changing the prototype of an existing object.

function myFactory(){};
myFactory.prototype = someOtherObject;

var newChild = new myFactory;
newChild.__proto__ === myFactory.prototype === someOtherObject; //true

Objects have an internal [[prototype]] property which points to the current prototype. The way it works is whenever a property on an object is called it will start at the object and then go up through the [[prototype]] chain until it finds a match, or fail, after the root Object prototype. This is how Javascript allows for runtime building and modification of objects; it has a plan for searching for what it needs.

The __proto__ property exists in some implementations (a lot now): any Mozilla implementation, all the webkit ones I know of, some others. This property points to the internal [[prototype]] property and allows modification post-creation on objects. Any properties and functions will instantly switch to match the prototype due to this chained lookup.

This feature, while being standardized now, still is not a required part of JavaScript, and in languages supporting it has a high likelihood of knocking your code down into the "unoptimized" category. JS engines have to do their best to classify code, especially "hot" code which is accessed very often, and if you're doing something fancy like modifying __proto__, they won't optimize your code at all.

This posts https://bugzilla.mozilla.org/show_bug.cgi?id=607863 specifically discusses current implementations of __proto__ and the differences between them. Every implementation does it differently, because it's a hard and unsolved problem. Everything in Javascript is mutable, except a.) the syntax b.) host objects (the DOM exists outside Javascript technically) and c.) __proto__. The rest is completely in the hands of you and every other developer, so you can see why __proto__ sticks out like a sore thumb.

There is one thing that __proto__ allows for that is otherwise impossible to do: the designation of an objects prototype at runtime separate from its constructor. This is an important use case and is one of the primary reasons __proto__ isn't already dead. It's important enough that it's been a serious discussion point in the formulation of Harmony, or soon to be known as ECMAScript 6. The ability to specify the prototype of an object during creation will be a part of the next version of Javascript and this will be the bell indicating __proto__'s days are formally numbered.

In the short term, you can use __proto__ if you're targeting browsers that support it (not IE, and no IE ever will). It's likely it'll work in webkit and moz for the next 10 years as ES6 won't be finalized until 2013.

Brendan Eich - re:Approach of new Object methods in ES5:

Sorry, ... but settable __proto__, apart from the object initialiser use case (i.e., on a new object not yet reachable, analogous to ES5's Object.create), is a terrible idea. I write this having designed and implemented settable __proto__ over 12 years ago.

... the lack of stratification is a problem (consider JSON data with a key "__proto__"). And worse, the mutability means implementations must check for cyclic prototype chains in order to avoid ilooping. [constant checks for infinite recursion are required]

Finally, mutating __proto__ on an existing object may break non-generic methods in the new prototype object, which cannot possibly work on the receiver (direct) object whose __proto__ is being set. This is simply bad practice, a form of intentional type confusion, in general.

Assigning an object prototype after creation

Is there a way to change an object's prototype after it has been instatiated?

Yes, there is: Object.setPrototypeOf (ES6 only), the counterpart of Object.getPrototypeOf - which access an object's real prototype, not just the .prototype property. There is also the .__proto__ getter/setter property (deprecated) that does the same (see Quick Javascript inheritance: Understanding __proto__).

However, please notice that it is usually a terrible idea to do that. Not only because there may be engines that don't support these, but it defeats all the fancy optimisations an engine uses of instances: Why is mutating the [[prototype]] of an object bad for performance?.

Changing prototype of an object which was created with literal initialization

With the current spec, you can't change an object's prototype once it's instantiated (as in, swap out one and swap in another). (But see below, things may be changing.) You can only modify the object's prototype. But that may be all you want, looking at your question.

To be clear about the distinction:

var p1 = {
foo1: function() {
console.log("foo1");
}
};
var p2 = {
foo2: function() {
console.log("foo1");
}
};

var o = Object.create(p1);
o.foo1(); // logs "foo1"
o.foo2(); // ReferenceError, there is no foo2
// You cannot now *change* o's prototype to p2.
// You can modify p1:
p1.bar1 = function() { console.log("bar1"); };
// ...and those modifications show up on any objects using p1
// as their prototype:
o.bar1(); // logs "bar1"
// ...but you can't swap p1 out entirely and replace it with p2.

Getting back to your question:

If u was created with a constructor like this...Then whatever I added to the prototype of U would automatically be added to every object that is created after those changes. But how do I get the same effect with Object literals?

By modifying the object you passed into Object.create as the prototype, as above. Note how adding bar1 to p1 made it available on o, even though o was created before it was added. Just as with constructor functions, the prototype relationship endures, o doesn't get a snapshot of p1 as of when it was created, it gets an enduring link to it.


ES.next is looking likely to have the "set prototype operator" (<|), which will make it possible to do that. But there's no standard mechanism for it currently. Some engines implement a pseudo-property called __proto__ which provides this functionality now, but it's not standardized.

Good way to change an object's prototype to change results of instanceof?

I see no possibility to do that. As RobG demonstrated, you can make instanceof returning false by changing harming the class's prototype property.

Seeing that, I thought you could do it with an extra class, you know, like the F from the common Object.create shim:

Object.newChangeable = function create(b) {
function F(){b.call(this);}
F.prototype = b.prototype;
var f = new F();
f.change = function change(n) {
F.prototype = n.prototype;
};
return f;
}

var obj = Object.newChangeable(Base);
obj instanceof Base; // true

obj.change(Derived);

But no:

obj instanceof Derived; // still false
obj instanceof Base; // still true

because the internal [[Prototype]] of obj still points to the same as Base.prototype. What you can do is making Derived the new Base:

var obj = new Base;
obj instanceof Base; // true

Derived.prototype = Base.prototype;
Base.prototype = {}; // something else

alert(obj instanceof Derived); // true
alert(obj instanceof Base); // false

But I don't think that is what you wanted, manipulating the right side of the expression instead of changing something at obj :-)

Setting prototype on object created through Object.create

Ok. so just for delete noise code, you are asking about this code:

var Model = {
prototype: {},
create: function() {
var object = Object.create(this);
object.prototype = Object.create(this.prototype);
return object;
}
};

The first line of Model.create() function is easy, it creates a new object who extends (prototypes) Model.

But you are counfused because the next line overwrites the object "prototype" property. It's not the object's prototype, the object prototype is still Model and it's stored on a hidden property called [[Prototype]], the property what the code is modifying has nothing to do with the object's [[Prototype]] it only has the same name. Let's change the name to understand it and it will be the same:

var Model = {
blablabla: {},
create: function() {
var object = Object.create(this);
object.blablabla = Object.create(this.blablabla);
return object;
}
};

It extends Model.blablabla so when you change object.blablabla it does not affect Model.blablabla.

var SubModel = Model.create();
SubModel.blablabla.newMethod = function() { };
console.log(Model.blablabla.newMethod); // undefined

Many times choose the right name for a field is more important than what it looks like.

Javascript: creation of object from an already instantiated object versus the prototype

myObject.myProperty = new myObject.AnotherObject();

differ to

myObject.myProperty = new TestObject.prototype.AnotherObject();

There's no difference at all. Remember, objects in JavaScript have a prototype chain. When you call new myObject.AnotherObject(); the engine will first check for a AnotherObject on myObject itself. Failing to find it, it will check on myObject's prototype, which it will find. The second version

myObject.myProperty = new TestObject.prototype.AnotherObject();

Just goes right to the place where AnotherObject is defined.


TestObject.prototype.AnotherObject = function () {
this.AnotherObject = new TestObject.prototype.AnotherObject();
}
myObject.AnotherObject();

Just walk through the code. When you say: myObject.AnotherObject();, AnotherObject will be called, with this set to myObject. The first line of that will attempt to create a new property on myObject (which is this) by setting it to the result of

new TestObject.prototype.AnotherObject(); 

which will then re-enter the very same AnotherObject function, but this time with this set to a new object whose prototype is set to TestObject.prototype.AnotherObject's prototype. And so on ad infinitum


Finally,

TestObject.prototype.createAnObject = function() {
this.AnotherObject = new TestObject.prototype.AnotherObject();
}
myObject.createAnObject();

Will not cause an infinite loop, so far as I can tell, and as far as I can test: FIDDLE

Basically, createAnObject will enter with this set to myObject. Inside of which a brand new property called AnotherObject will be created on myObject, which will be set to a new invocation of the AnotherObject function you previously set up.

Note that after this call is made, the AnotherObject function will still exist, but, it will be shadowed by the AnotherObject property you just created. So now you'll never ever be able to say

var f = new myObject.AnotherObject()

Because you now have a AnotherObject property sitting right on myObject, which will be found and returned before anything on the prototype is ever checked.

Well, I mean, you could always say delete myObject.AnotherObject and remove that property from the object, which would then open you up to the AnotherObject being found on the prototype, but really, you should avoid name conflicts like this to begin with.


Regarding your last bit of code

A) Why not make User its own function?

B) Why not set up this.User = new ...() right in the ClientObject constructor function? That way you wouldn't need the undefined check
C) ClientObject should be defined as

function ClientObject(){...` 

the you have it now seems to be creating an implicit global.



Related Topics



Leave a reply



Submit