Good way to change an object's prototype to change results of instanceof?
-
27-05-2021 - |
Domanda
I wanted to comment on this old question, but it appears to be locked.
Here is my use case:
- An object
obj
is created with constructorBase
.obj instanceof Base
returns true. - I want to change the prototype of
obj
such that it appears as ifobj
was constructed fromDerived
. That is, I wantobj
to get access to the methods ofDerived
obj instanceof Derived
to return true
The reason is that obj
is to have a type in the hierarchy that is unknown at the time of its creation and determined by what happens after. I want to be able to move it down the hierarchy.
I believe I can do this with
obj.__proto__ = Derived.prototype;
but __proto__
will be deprecated in the next version of JavaScript. The proxies API, which has changed since the question I linked above was asked, does not seem to support my use case.
Is there an alternative implementation for my use case that exists now or is planned for the future?
The only alternative I can see right now is to use
obj2 = Object.create(Derived.prototype);
obj2.extend(obj);
and never store more than one reference to obj, but that cost is quite an inconvenience.
Here is a fiddle demonstrating the issue.
Soluzione
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
:-)
Altri suggerimenti
The instanceof operator just checks if a constructor's public prototype
property is on an object's [[Prototype]]
chain. One way to break the chain is to change the constructor's prototype:
function Base() {}
var base = new Base();
alert( base instanceof Base); // true
Base.prototype = {};
alert( base instanceof Base); // false
alert( base instanceof Object); // true
The second alert is false because the new Base.prototype
is not on the [[Prototype]]
chain of base
any more (the original one still is though). Note that Object.protoyype
still is. The above is one reason why the instanceof
operator is not seen as being particularly useful.
To do what you are trying to do, you must create the [[Prototype]]
chain when the object is constructed because you can't change it later:
Derived.prototype = new Base();
var base = new Derived();
alert(base instanceof Base); // true
alert(base instanceof Derived); // true
Edit
The requirements were:
- An object obj is created with constructor Base. obj instanceof Base returns true.
As shown, that isn't necessarily true. If you have a strategy that depends on instanceof
returning a particular value then you are placing a (probably unreasonable) constraint on the design with no clear benefit.
2
. I want to change the prototype of obj such that it appears as if obj was constructed from Derived. That is, I want•obj to get access to the methods of Derived
you can do that by making Base.prototype an instance of Derived (as shown) or by copying the properties to Base.prototype.
•obj instanceof Derived to return true
You can do that by making Base.prototype
an instance of Derived before creating any instances of Base.
You can't modify the chain after an instance is created. If you drop the instanceof
constraint, then you can add the methods of Derived.prototype
by simply copying them to Base.prototype
. Another way is using call or apply:
Derived.prototype.someMethod.call(base, ...);
but I suspect you are trying to do something that is not possible.
Did you mean this (a little bit confused about the question and I'm still a learner)
function vehicle(name, color)
{
if(this instanceof arguments.callee)
{
this.name = name;
this.color = color;
}
else return new arguments.callee(arguments);
}
vehicle.prototype = {
getName : function()
{
alert(this.name);
}
}
function Car(name, color, speed)
{
this.name = name;
this.color = color;
this.speed = speed;
}
Car.prototype = vehicle();
var obj = new Car("Ford", "Red", "200mph");
obj.getName(); // alerts `Ford` using vehicle's/parent's method
alert(obj instanceof vehicle); alerts `True` instanceof vehicle's/parent's constructor
A fiddle is here.
An article by John Resig.