Tutorial :Javascript inheritance - instanceof not working?



Question:

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.


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

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.


Solution:4

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/


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »