Лучшие практики для определения типа объектов в Javascript - PullRequest
15 голосов
/ 15 октября 2011

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

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

Спасибо.

Ответы [ 5 ]

5 голосов
/ 15 октября 2011

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

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

  1. Если все, о чем вы говорите, это «реализует ли этот объект конкретный точечный интерфейс», то вы можете выполнить проверку объектов напрямую:

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

    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"); };
    
  3. Если вам действительно нужен какой-то тег, вы можете сделать его явно, как уже указывалось в некоторых других ответах:

    Point2D.prototype.pointType = "2D";
    

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

3 голосов
/ 15 октября 2011

Мне интересно, если вы хотите что-то намного проще, как это:

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"

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

1 голос
/ 15 октября 2011

Typeof сообщает вам, только если переменная 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' 
0 голосов
/ 15 октября 2011

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

На самом деле в 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);
    }
}
0 голосов
/ 15 октября 2011

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

{
    X: x,
    Y: y
}

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

Кстати, вы ищете p instanceof Point2D.

Редактировать:

Вам не нужен этот паттерн, не так ли?

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
...