在 Javascript 中确定对象类型的最佳实践

Posted

技术标签:

【中文标题】在 Javascript 中确定对象类型的最佳实践【英文标题】:Best practice for determining objects type in Javascript 【发布时间】:2011-12-08 04:02:18 【问题描述】:

如果你在 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'

这是一种可行的方法吗(优点/缺点是什么?)还是我错过了更好的做法?

谢谢。

【问题讨论】:

Point2D(1,1).name 在 FF 或 chrome 中似乎不起作用。 它在 Chrome 中适用于我,但我不知道这是否是最好的方法。 它依赖于函数签名吗?即 var Point2d = function Point2D() ? @SeanThoman Function.prototype.name 不是标准的,因此在某些浏览器中将无法正确实现或根本无法实现。 看我的编辑,我想这就是你想要的。 【参考方案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();

现在我们可以处理有关获取类型名称的部分了。您缺少的更好的做法是首先不检查类型。从 OO 的角度来看,对象是如何实现的(ts 类)并不重要,重要的是它的行为方式以及它的接口是什么(公开的方法和属性)。此外,从 Javascript 的角度来看,类型名称是一种二等 hack,与实际使用的原型 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";
    

    这里的优点是,如果需要,您可以让多个类具有相同的“类型”,并且您不必担心任何棘手的instanceoftypeof 极端情况。

【讨论】:

谢谢你,非常有帮助和详尽,尤其是关于查看界面而不是类型的观点。干杯!【参考方案2】:

我想知道你是否想要像这样更简单的东西:

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


var p = new Point2D(1,1);

alert(p instanceof Point2D);    // true

编辑:

您可以添加自己的类型属性:

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


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

关于为什么要知道对象的类型,您没有说太多。我建议您可能要重新考虑这一点。只要有可能,特定类型的方法处理应该放在对象之间的公共方法中,这样您只需在方法调用中请求一个操作,并且该方法在每个特定对象的方法中实现特定类型的处理。

【讨论】:

我的情况希望函数返回一个匿名对象。否则这会很好..但是我可以将它变成一个解决方案。 如果函数返回一个匿名对象,那么它的类型就是一个匿名对象。你不能在这里两者兼得。如果您希望它成为 Point2D 的实例,它实际上必须是 Point2D 的实例,而不是匿名对象。选择你真正想要的。为什么它需要是一个匿名对象?我的 Point2D 函数对象和您的匿名对象都具有相同的属性 .X.Y。 Point2D 的不同之处在于您可以修改它的原型,以便新实例具有自定义方法。而且,您可以使用instanceof 来识别它。 您还可以添加自己的属性来标识类型。我在答案中添加了一个示例来说明这一点。 @jfriend00,我想使用一个匿名对象,以便返回的对象可以具有关闭外部函数中的变量的函数。因此,是的,必须向匿名对象添加一些东西以指示其类型,除非还有其他我不知道的技巧。将所述指标添加到匿名对象的那个或更好的做法。 @jfriend00,我也可以将内部函数命名为与外部函数相同的名称,这可能会起作用..【参考方案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

编辑:

你不会碰巧在寻找这种模式吧?

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

【讨论】:

以上是关于在 Javascript 中确定对象类型的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

PHP ORM 最佳实践:从另一个对象类型中检索值:

JavaScript设计模式与开发实践 面向对象

JavaScript设计模式与开发实践第一部分

WEB前端开发最佳实践系列JavaScript篇

如何保持 html DOM 元素与关联的 javascript 对象同步?最佳实践

JavaScript 中长构造函数的最佳实践