views:

149

answers:

3

I define how two functions can inherit from each other as follows:

Function.prototype.inherit = function(parent){

function proto() {}
proto.prototype = parent.prototype;

this.prototype = new proto();
this.prototype.constructor = this;
this.prototype.parent = parent;

}

I then need to define an isInstance function that would behave like Java's instanceOf or PHP's instanceof. In essence, isInstance could be used to determine whether a variable is an instantiated object of a function that inherits from a parent function.

This is what I wrote:

Function.prototype.isInstance = function(func){
if(this == func){
 return true;
} else{
 if (this.prototype.parent == undefined || this.prototype.parent == null) {
  return false; 
 } else {
  return this.prototype.parent.isInstance(func);   
 }
}

}

Which works fine when comparing two functions, but not when comparing instantiated variables.

Object2.inherit(Object1);

Object2.isInstance(Object1); //returns true

var obj2 = new Object2();

obj2.isInstance(Object1);// obj2.isInstance is not a function

The last case above is what I want to get working. How can I add isInstance to instances, not just functions? And to all javascript gurus out there, are there any improvements to my code (the inherit method, maybe)?

Thanks.

+4  A: 

Why do you need to do this? JavaScript doesn't have a notion of a strict class hierarchy for a reason: it really can't be done. You can probably get to a solution that works most of the time, but I'm not sure you can cover all the edge cases. Additionally, since JavaScript doesn't have any notion of interfaces, testing for explicit inheritance leads to high coupling, and would seem to violate the SOLID principles.

A much better (and more idiomatic) way of approaching this problem is to use duck typing. Simply test for the existence of the method you need, and then call it. Just be prepared to catch an exception if necessary (which you should be doing anyway).

Daniel Pryden
Ah, I think everyone tries to write their own OO system in javascript at one point or another. It's a lot of fun - really shows the true flexibility of the language. I don't think anyone really does this for production - it's all about the fun.
x0n
+1 - Interesting perspective. But still I'd like to know how to do this (even if, as you said, the solution can't cover the edges).
jd
+1  A: 

To leave the good design argument aside for a second and focus on your problem, the issue is that you set isInstance() to be a method on Function's prototype. This means Objects that are Functions will have it, while Objects that are not (such as Object1 and Object2 in your example) will not.

I suggest that instead of trying to implement it as a method, you have it be a static function:

function isInstance(object, ofClass) {
  // Same logic as you have, except use 
  // object.prototype instead of this.prototype
}

You can then invoke it as

isInstance(obj2, Object2);
levik
+2  A: 

I then need to define an isInstance function that would behave like Java's instanceOf or PHP's instanceof.

What is wrong with JavaScript's own instanceof?

Given your inherit function,

function Shape() {};
function Square() {};
Square.inherit(Shape);
alert(new Square() instanceof Shape); // true

instanceof works by checking whether the given constructor's prototype property is in the instance's prototype chain. So eg.

function A() {};
function B() {};
var b= new B();
A.prototype= B.prototype;
alert(b instanceof A); // true

This is why instanceof Shape works even though Square.prototype was not created using new Shape; instead it was created by new proto(), but since both proto and Shape share the same prototype property, JavaScript can't tell the difference.

You can't normally see the prototype chain (although Firefox and WebKit make it available through the __proto__ property), but it is the way inheritance is actually handled in JS; the visible Function.prototype property is only used to set up the initial state of the prototype chain at new-time.

How can I add isInstance to instances, not just functions?

Object.prototype.isInstance(c)= function() {
    return this instanceof c;
}

I'm not sure this really gets you much though! And prototyping into Object is often considered in poor taste, as it affects literally every object and can confuse other scripts using Object for a lookup mapping.

are there any improvements to my code (the inherit method, maybe)?

I like your inherit method, it's one of the more lightweight and JavaScripty ways of making an object system.

With instanceof you could lose the parent and constructor properties, unless you wanted them for other convenience purposes. I'd put the parent on the constructor function though (so it's like a subclass knowing its base class), and I'd call constructor something else (there is already property called constructor in Firefox but it doesn't do what you think and is generally best avoided).

The only drawback I see is that you can't inherit the constructor function, so every time you make a new subclass you must write a constructor that calls the base constructor, even if your subclass's constructor does nothing at all:

function Square() {
    Shape.apply(this, arguments);
}
Square.inherit(Shape);
bobince