Лучшая практика определения типа объектов в 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);
}
}