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
How to Reload or Re-Render the Entire Page Using Angularjs
What Is This Practice Called in JavaScript
Indirect Function Call in JavaScript
Why Does Isnan(" ") (String with Spaces) Equal False
Need to Cancel Click/Mouseup Events When Double-Click Event Detected
How to Access 'Window' (Target Page) Objects When @Grant Values Are Set
How to Merge Two JavaScript Objects Together in Es6+
Short-Polling VS Long-Polling for Real Time Web Applications
Inspect Attached Event Handlers for Any Dom Element
How to Disable JavaScript in Chrome Developer Tools
Using Async/Await Inside a React Functional Component
Node.Js Shell Command Execution
How to Rotate a 3D Object on Axis Three.Js
What's the Best Way to Loop Through a Set of Elements in JavaScript
JavaScript - How to Get the Url of Script Being Called
Es6 Promise.All() Error Handle - Is .Settle() Needed