Лучшая практика определения типа объектов в Javascript

Если у вас есть экземпляр объекта в javascript, кажется, что его трудно найти, например,

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

var p = new Point2D(1,1);

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

Один из способов, который я нашел, заключался в том, чтобы сделать объект своим собственным прототипом, а затем вы можете получить его имя эффективно, вызвав 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'

Будет ли это ОК способ сделать это (какие плюсы/минусы?) или есть лучшая практика, которую я упускаю?

Спасибо.

Ответы

Ответ 1

Сначала нам нужно исправить то, как вы строите свой класс, поскольку вы спотыкаетесь в некоторых подводных камнях JS и делаете некоторые действительно странные вещи:

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();

Теперь мы можем заняться частью получения имен типов. Лучшая практика, которую вам не хватает, - не проверять тип в первую очередь. С точки зрения ОО не должно иметь значения, как реализован объект (класс ts), но как он ведет себя и каким его интерфейсом (открытые методы и свойства). Кроме того, с точки зрения Javascript, имена типов - это взломы второго класса, которые не очень хорошо вписываются в прототипную схему OO, которая фактически используется.

Поскольку есть много причин, по которым вы могли бы попытаться проверить имя типа, в первую очередь, существует множество разных "лучших" решений. Некоторые, о которых я мог подумать:

  • Если все, о чем вы говорите, это "реализует ли этот объект определенный интерфейс точки", вы можете напрямую выполнить функцию-проверку:

    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);
    }
    
  • Если вы используете имена типов для диспетчеризации, используйте вместо этого метод. Его естественный путь OO.

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

    становится

    Cat.prototype.speak = function(){ console.log("meow"); };
    Dog.prototype.speak = function(){ console.log("woof"); };
    
  • Если вам действительно нужен какой-то тег, вы можете явно сделать его, как уже указывали некоторые другие ответы:

    Point2D.prototype.pointType = "2D";
    

    Преимущества здесь в том, что у вас может быть более одного класса с тем же "типом", если вам нужно, и что вам не нужно беспокоиться о каких-либо сложных случаях instanceof или typeof.

Ответ 2

Мне интересно, хотите ли вы что-то гораздо более простое:

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

var p = new Point2D(1,1);

alert(p instanceof Point2D);    // true

Edit:

Вы можете добавить свой собственный тип свойства:

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

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

Вы не много говорили о том, почему вы хотите узнать тип объекта. Я бы предположил, что вы, возможно, захотите переосмыслить это. Всякий раз, когда это возможно, типизированная обработка методов должна быть включена в общие методы среди ваших объектов, поэтому вы просто запрашиваете действие в вызове метода, а метод реализует специфичную для типа обработку в каждом конкретном методе объекта.

Ответ 3

Typeof сообщает вам, только если var x:

  • строка
  • число
  • логическое
  • объект (любой объект, функция, массив... на самом деле все остальное)

Теперь variable.constructor сообщает, какая функция фактически создала текущий объект.

Также существует разница:

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' 

Ответ 4

Вы не возвращаете Point2D, но это:

{
    X: x,
    Y: y
}

который явно является объектом.

То, что вы ищете, кстати, p instanceof Point2D.

Edit:

Вы, похоже, не ищете этот шаблон, не так ли?

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

Ответ 5

Ну, это сложно. Вы можете начать с этой функции:

На самом деле в jQuery есть много отличных функций. Посмотрите на все функции утилиты, которые начинаются с "is": http://api.jquery.com/category/utilities/

Если они недостаточно хороши, найдите здесь исходный код и напишите свои собственные варианты: http://james.padolsey.com/jquery/#v=1.6.2

Также вы можете использовать эту функцию:

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

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