简体   繁体   中英

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.

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.

I'm wondering if you want something much simpler like this:

function Point2D(x, y) {
    this.X = x;
    this.Y = y;
}

var p = new Point2D(1,1);

alert(p instanceof Point2D);    // true

Edit:

You could add your own type property:

function Point2D(x, y) {
    this.X = x;
    this.Y = y;
    this.type = "Point2D";
}

var p = new Point2D(1,1);
alert(p.type);   // "Point2D"

You haven't said much about WHY you want to know the type of an object. I would suggest that you might want to rethink that. Whenever possible, type-specific handling of methods should be put into common methods among your objects so that you just ask for an action in a method call and the method implements the type-specific handling in each specific object's method.

Typeof tells you only if var x is:

  • a string
  • a number
  • a boolean
  • an object (any object, function, array ... actually everything else)

Now variable.constructor tells you which function actually created current object.

There also is a difference:

var foo = function Test(){

};

var x = new foo();
// Here, x.constructor == function Test(){};
// and x.constructor.name == 'Test'

function Bar(){

}

var y = new Bar();
// But here, y.constrcutor == 'Bar' 

You're not returning Point2D but this:

{
    X: x,
    Y: y
}

which clearly is an object.

What you're looking for, by the way, is p instanceof Point2D .

Edit:

You don't happen to be looking for this pattern, do you?

function Point2D(x, y) {
    var self = this;

    this.X = x;
    this.Y = y;

    this.getX = function(){
        alert(this.X);
    }
}

var p = new Point2D(1,1);
p.getX();
alert(p instanceof Point2D);    // true

Well, it's complicated. You can start with this function:

Actually there are a lot great functions in jQuery. Look at all the utility functions that start with "is" : http://api.jquery.com/category/utilities/

If those aren't good enough, look up their source code here and write your own variations: http://james.padolsey.com/jquery/#v=1.6.2

Also you can use this function:

function listProps (obj) {
    console.log("constructor: "+obj.constructor);
    console.log("prototype "+obj.prototype);

    for (var prop in obj) {
        console.log(prop+": "+obj.prop);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM