3

I wanted to comment on this old question, but it appears to be locked.

Here is my use case:

  1. An object obj is created with constructor Base. obj instanceof Base returns true.
  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
    • 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.

Community
  • 1
  • 1
John Freeman
  • 2,552
  • 1
  • 27
  • 34
  • `__proto__` is [already deprecated](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/Proto). You can only modify an object's `[[Prototype]]` when you construct it, you can't modify the chain afterward. – RobG Mar 28 '12 at 00:16
  • Ok... and yet it is still available in my environment. I am looking for a more portable solution. Can you help with that? – John Freeman Mar 28 '12 at 00:19
  • Something that is deprecated isn't removed from the system, it is essentially marked for future deletion and therefore should not be used. Support is maintained to allow time for code that uses deprecated features to be modified. – RobG Mar 28 '12 at 00:40
  • Yes, and what is the point of mentioning that? I already mentioned I am currently relying on non-portable behavior and am searching for an alternative. Your comment has contributed nothing to the discussion. – John Freeman Mar 28 '12 at 00:55
  • Ok, it seemed you were surprised to find a deprecated feature still available, just thought I'd explain. – RobG Mar 28 '12 at 02:58

3 Answers3

4

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 :-)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Ok, I think you could have stopped after the first sentence. I'm going to give some more time for other ideas, but I'll accept this if no one else can come up with a solution. – John Freeman Mar 28 '12 at 01:00
2

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:

  1. 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.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Yes, I would have done that too. But I thought the question was about specific instances, and this solution changes the prototypes of the whole "class". – Bergi Mar 28 '12 at 00:15
  • Bergi is correct. Please see the [fiddle](http://jsfiddle.net/hJzC7/1/) I added to the question. – John Freeman Mar 28 '12 at 00:22
  • But your fiddle doesn't do the above, it assigns a new prototype to Derived but then still makes an instance of Base (which does not connect the prototypes as you want). – RobG Mar 28 '12 at 03:12
  • Derived gets a prototype assigned so that instances of it will be derived from Base. It changes the prototype chain of obj to accomplish the two criteria I specified. – John Freeman Mar 28 '12 at 05:25
0

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.

The Alpha
  • 143,660
  • 29
  • 287
  • 307
  • No, the question was "how to make a `new car()` instanceof `plane`?"! – Bergi Mar 28 '12 at 00:00
  • @ Bergi, Here vehicle is base constructor and car is derived from vehicle and Ford is an object created by car , so what did you mean by plane here, please ? – The Alpha Mar 28 '12 at 00:02
  • Sheikh, I thought I explained clearly in the original question, but I will try again: You created obj as an instance of Car. Now create a new class Dragster that derives from Car, and change obj (but not its construction from Car) so that this expression returns true: "obj instanceof Dragster" – John Freeman Mar 28 '12 at 00:11
  • Let me grasp it, will be back soon. – The Alpha Mar 28 '12 at 00:24
  • But then I cannot use a different prototype for Dragster without changing Car, so no, it is not ok. – John Freeman Mar 28 '12 at 00:56
  • +1, what you're doing there is just renaming Car to Dragster. – Bergi Mar 28 '12 at 00:59
  • If you find any better answer please inform me, heerasheikh@ymail.com and all the best. – The Alpha Mar 28 '12 at 00:59
  • Learning is not over my friend, a long way to go, hope I can learn something new from this one. Everyday I'm learning something new from So. – The Alpha Mar 28 '12 at 01:02