Javascript inheritance - instanceof not working?

16,253

Solution 1

These days you shouldn't need .prototype = new Thing(), I think I'm late to the party but you can just use Object.create on the prototype of the parent and then override the methods you're interested in overriding. An example:

var IDataSource = function(){
    throw new Error("Not implemented, interface only");
};

IDataSource.prototype.getData = function(){
    throw new Error("Not implemented.");
};

var BasicDataSource = function(){};
BasicDataSource.prototype = Object.create(IDataSource.prototype);
BasicDataSource.prototype.getData = function(){
    //[do some stuff, get some real data, return it]
    return "bds data";
};

var MockDataSource = function(){};
MockDataSource.prototype = Object.create(IDataSource.prototype);
MockDataSource.prototype.getData = function(){
    //[DONT DO some stuff return mock json]
    return "mds data";
};

MockDataSource.prototype.getDataTwo = function(){
    //[DONT DO some stuff return mock json]
    return "mds data2";
};


var MockDataSource2 = function(){};
MockDataSource2.prototype = Object.create(MockDataSource.prototype);




var bds = new BasicDataSource();
console.log("bds is NOT MockDataSource:", bds instanceof MockDataSource);
console.log("bds is BasicDataSource:", bds instanceof BasicDataSource);
console.log("bds is an IDataSource:", bds instanceof IDataSource);
console.log("bds Data:", bds.getData());


var mds = new MockDataSource();
console.log("mds is MockDataSource:", mds instanceof MockDataSource);
console.log("mds is NOT a BasicDataSource:", mds instanceof BasicDataSource);
console.log("mds is an IDataSource:", mds instanceof IDataSource);
console.log("mds Data:", mds.getData());
console.log("mds Data2:",mds.getDataTwo());


var mds2 = new MockDataSource2();
console.log("mds2 is MockDataSource2:", mds2 instanceof MockDataSource2);
console.log("mds2 is MockDataSource:", mds2 instanceof MockDataSource);
console.log("mds2 is NOT a BasicDataSource:", mds2 instanceof BasicDataSource);
console.log("mds2 is an IDataSource:", mds2 instanceof IDataSource);
console.log("mds2 Data:", mds2.getData());
console.log("mds2 Data2:",mds2.getDataTwo());

If you run this code in node you will get:

bds is NOT MockDataSource: false
bds is BasicDataSource: true
bds is an IDataSource: true
bds Data: bds data
mds is MockDataSource: true
mds is NOT a BasicDataSource: false
mds is an IDataSource: true
mds Data: mds data
mds Data2: mds data2
mds2 is MockDataSource2: true
mds2 is MockDataSource: true
mds2 is NOT a BasicDataSource: false
mds2 is an IDataSource: true
mds2 Data: mds data
mds2 Data2: mds data2

No worry about parameters to constructors or any such craziness.

Solution 2

The problem is that the copyPrototype function only copies the properties from a constructors prototype to another one, for example, at the end, the intenal [[Prototype]] link of C.prototype simply points to Object.prototype.

The prototype chain of instC and constructor's prototypes look like this:

                [[Prototype]]
    A.prototype -------------->|-------------------|
                               |                   |
    B.prototype -------------->|  Object.prototype | ---> null
                               |                   |
    C.prototype -------------->|-------------------|
        ^
        |
      instC

The instanceof operator traverses the prototype chain, your instC object, as you can see, will have on its prototype chain only C.prototype and Object.prototype.

You can achieve what you want by setting your constructor's prototypes to be object instances of their "parent" constructors, for example:

function A() {
  this.Name = 'Class A'
}

A.prototype.PrintName = function () {
  alert(this.Name);
}

function B() {
  //..
}
B.prototype = new A();
B.prototype.constructor = B; // fix constructor property


function C() {
  //..
}

C.prototype = new B();
C.prototype.constructor = C; // fix constructor property

var instC = new C();
if (instC instanceof A)
  alert('horray!');

Now the prototype chain of the instC object looks like this:

           ---------------        ---------------        ---------------
 instC --> | C.prototype | -----> | B.prototype | -----> | A.prototype |
           ---------------        ---------------        ---------------
                                                                |
                                                                V
                                                       --------------------
                                                       | Object.prototype |
                                                       --------------------
                                                                |
                                                                V
                                                               null

Recommended article:

Solution 3

Recently found a nice implementation of OO javascript by John Resig (The jQuery Guy!) which I shall be using for future projects; http://ejohn.org/blog/simple-javascript-inheritance/

Solution 4

Ok I've found a solution which keeps the instanceof function working, as well as allowing me to pass constructor parameters through the inheritance chain. The solution is detailed here; https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Model - my class structure now looks like this;

function A(p) {
 this.Position = p || new Vector2d(0,0);
}

function B(p) {
 this.base = A;
 this.base(p);
}
B.prototype = new A;

function C(p) {
 this.base = B;
 this.base(p);
}
C.prototype = new B;

if(C instanceof A)
  alert (' it worked!! '); // you now see this alert box!

Thanks CMS for highlighting to me why this wasn't working!!

You can check out the full project (well an older build which, at time of writing has yet to see this new OO method put in the whole way through) up at http://8weekgame.shawson.co.uk/ - just check out my latest posts.

Share:
16,253
Shawson
Author by

Shawson

I'm a web and mobile developer- current in employment as a Senior Developer at LinnWorks.linkedin

Updated on June 15, 2022

Comments

  • Shawson
    Shawson about 2 years

    I'm writing a simple platform game using javascript and html5. I'm using javascript in an OO manner. To get inheritance working i'm using the following;

    // http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/
    function copyPrototype(descendant, parent) {
        var sConstructor = parent.toString();
        var aMatch = sConstructor.match(/\s*function (.*)\(/);
        if (aMatch != null) { descendant.prototype[aMatch[1]] = parent; }
        for (var m in parent.prototype) {
            descendant.prototype[m] = parent.prototype[m];
        }
    };
    

    For the sake of this post consider the following example;

    function A() {
     this.Name = 'Class A'
    }
    A.prototype.PrintName = function () {
     alert(this.Name);
    }
    
    function B() {
     this.A();
    }
    copyPrototype(B, A);
    
    function C() {
     this.B();
    }
    copyPrototype(C, B);
    
    var instC = new C();
    
    if (instC instanceof A)
      alert ('horray!');
    

    As I understand it I would expect to see a horray alert box, because C is an instance of C & B & A. Am I wrong? Or am I just using the wrong method to check? Or has copyPrototype knackered the instanceof operator?

    Thanks as always for taking the time to read this!

    Shaw.