Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for determining objects type in Javascript

If you have an instance of an object in javascript, it seems it that can be difficult to find its actual type, ie

var Point2D = function Point2D(x, y) {
  return {
    X: x,
    Y: y
  }
}

var p = new Point2D(1,1);

typeof p // yields just 'Object' not 'Point2D'

One way around it I found was to make the object its own prototype, and then you can gets its name effectively by calling prototype.constructor.name,

var Point2D = function Point2D(x, y) {
  return {
    X: x,
    Y: y,
    prototype: this
  }
}

new Point2D(1,1).prototype.constructor.name // yields 'Point2D'

Would this be an OK way of doing it (what are the pros/cons?) or is there a better practice I am missing out on?

Thanks.

like image 716
Sean Thoman Avatar asked Oct 15 '11 00:10

Sean Thoman


1 Answers

First we need to fix how you are building your class, since you are tripping in some JS pitfalls and doing some really weird stuff:

function Point2D(x, y){
    //Our constructor/initialization function
    //when run, the 'this object will have
    //Point2D.prototype as its prototype

    this.x = x;
    this.y = y;

    //Don't return a value here! Doing so overrides
    //the default "return this" that we actually want.
}

//You can put things in the Point2D prototype in order to have them
//be shared by all Point2D objects. Normally you want the methods to be shared.
Point2D.prototype.getX = function(){
    return this.x;
};

//Note that there is nothing magical about the "prototype" property.
//It is just where the `new` syntax expects the prototype it will use to be.
//The actual magic property is __proto__ (don't depend on it though
// __proto__ is browser specific and unsafe/evil)

//We now can create points!
var p = new Point2D(17.0, 42.0);
p.getX();

Now we can tackle the part about getting the type names. The better practice you are missing on is not inspecting the type in the first place. From an OO perspective it shouldn't matter how an object is implemented (ts class) but how it behaves and what is its interface (exposed methods and properties). Also, from a Javascript perspective, type names are a second-class hack that don't fit very well with the prototypical OO scheme that is actually used.

Since there are many reasons you could be trying to inspect a type name in the first place, there are many different "best" solutions. Some that I could think of:

  1. If all you case about is "does this object implement a particular point interface" then you can do feature-inspection directly:

    function isPoint(p){
        //check if p has all the methods I expect a point to have:
        //(note that functions are truthy in a boolean context)
        return (p.getX && p.getY);
    }
    
  2. If you use the type names to do dispatching consider using a method instead. Its the natural OO way.

    function speak(obj){
        //fake pseudo-syntax:
        if(obj is of type Cat){ console.log("meow"); }
        if(obj is of type Dog){ console.log("woof"); }
    }
    

    becomes

    Cat.prototype.speak = function(){ console.log("meow"); };
    Dog.prototype.speak = function(){ console.log("woof"); };
    
  3. If you really need some sort of tag, you can explicitely make one, as pointed by some of the other answers already:

    Point2D.prototype.pointType = "2D";
    

    The advantages here are that you can have more than one class have the same "type" if you need to and that you don't have to worry about any of the tricky instanceof or typeof corner cases.

like image 108
hugomg Avatar answered Sep 20 '22 08:09

hugomg