题 如何在JavaScript中获取对象类型的名称?


有没有相当于Java的JavaScript class.getName()


1052
2017-12-01 22:06


起源




答案:


有没有相当于Java的JavaScript class.getName()

没有

ES2015更新的名字 class Foo {} 是 Foo.name。的名字 thing无论如何,上课 thing的类型,是 thing.constructor.name。 ES2015环境中的内置构造函数具有正确性 name 属性;例如 (2).constructor.name 是 "Number"


但是这里有各种各样的黑客都会以这种或那种方式落下来:

这是一个可以做你需要的黑客 - 要知道它修改了对象的原型,人们皱眉的东西(通常是有充分理由的)

Object.prototype.getName = function() { 
   var funcNameRegex = /function (.{1,})\(/;
   var results = (funcNameRegex).exec((this).constructor.toString());
   return (results && results.length > 1) ? results[1] : "";
};

现在,所有对象都将具有该功能, getName(),这将以字符串形式返回构造函数的名称。我已经测试了这个 FF3 和 IE7,我不能代表其他实施。

如果你不想这样做,这里讨论一下在JavaScript中确定类型的各种方法......


我最近更新了这个更详尽一点,尽管不是那样。更正欢迎......

使用 constructor 属性...

一切 object 有它的价值 constructor 财产,但取决于如何 object 构建以及您想要对该值做什么,它可能有用也可能没用。

一般来说,你可以使用 constructor 用于测试对象类型的属性,如下所示:

var myArray = [1,2,3];
(myArray.constructor == Array); // true

所以,这对大多数需求都有效。那说......

注意事项

不管用 在所有 在很多情况下

这种模式虽然破碎,却很常见:

function Thingy() {
}
Thingy.prototype = {
    method1: function() {
    },
    method2: function() {
    }
};

Objects 通过建造 new Thingy 会有一个 constructor 指向的财产 Object不是 Thingy。所以我们一开始就是正确的;你根本无法信任 constructor 在您无法控制的代码库中。

多重继承

一个不那么明显的例子是使用多重继承:

function a() { this.foo = 1;}
function b() { this.bar = 2; }
b.prototype = new a(); // b inherits from a

事情现在不像你期望的那样工作:

var f = new b(); // instantiate a new object with the b constructor
(f.constructor == b); // false
(f.constructor == a); // true

所以,如果你可能会得到意想不到的结果 object 你的测试有所不同 object 设为它 prototype。在本讨论的范围之外,有很多方法可以解决这个问题。

还有其他用途 constructor 财产,其中一些有趣,其他不是那么多;目前我们不会深入研究这些用途,因为它与本次讨论无关。

不会跨框架和跨窗口工作

运用 .constructor 当你想检查来自不同的对象类型时,类型检查会中断 window 对象,比如iframe或弹出窗口。这是因为每种核心类型都有不同的版本 constructor 在每个“窗口”中,即

iframe.contentWindow.Array === Array // false

使用 instanceof 运营商...

instanceof 运算符是一种干净的测试方式 object 类型,但有自己的潜在问题,就像 constructor 属性。

var myArray = [1,2,3];
(myArray instanceof Array); // true
(myArray instanceof Object); // true

instanceof 无法为文字值工作(因为文字不是 Objects

3 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false

文字需要包含在 Object 为了 instanceof例如,工作

new Number(3) instanceof Number // true

.constructor 检查对文字工作正常,因为 . 方法调用隐式地将文字包装在它们各自的对象类型中

3..constructor === Number // true
'abc'.constructor === String // true
true.constructor === Boolean // true

为什么两个点为3?因为Javascript将第一个点解释为小数点;)

不会跨框架和跨窗口工作

instanceof 也不会在不同的窗口工作,原因与之相同 constructor 财产检查。


使用 name 的财产 constructor 属性...

不起作用 在所有 在很多情况下

再次,见上文;它很常见 constructor 彻底,完全错误和无用。

不适用于<IE9

运用 myObjectInstance.constructor.name 会给你一个包含该名称的字符串 constructor 使用的功能,但受到关于的警告 constructor 前面提到的财产。

对于IE9及更高版本,您可以 猴子补丁支持

if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function() {
            var funcNameRegex = /function\s+([^\s(]+)\s*\(/;
            var results = (funcNameRegex).exec((this).toString());
            return (results && results.length > 1) ? results[1] : "";
        },
        set: function(value) {}
    });
}

更新后的版本 来自有问题的文章。这是在文章发表3个月后添加的,这是文章作者Matthew Scharley使用的推荐版本。这一变化的灵感来自于 评论指出潜在的陷阱 在前面的代码中。

if (Function.prototype.name === undefined && Object.defineProperty !== undefined) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function() {
            var funcNameRegex = /function\s([^(]{1,})\(/;
            var results = (funcNameRegex).exec((this).toString());
            return (results && results.length > 1) ? results[1].trim() : "";
        },
        set: function(value) {}
    });
}

使用Object.prototype.toString

原来,结果是 这篇文章详情, 您可以使用 Object.prototype.toString  - 低级别和通用实现 toString  - 获取所有内置类型的类型

Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]

人们可以写一个简短的辅助函数,如

function type(obj){
    return Object.prototype.toString.call(obj).slice(8, -1);
}

删除cruft并获取类型名称

type('abc') // String

但是,它会回来 Object 适用于所有用户定义的类型。


适合所有人的警告......

所有这些都存在一个潜在的问题,那就是如何构建有关对象的问题。以下是构建对象的各种方法以及不同类型检查方法将返回的值:

// using a named function:
function Foo() { this.a = 1; }
var obj = new Foo();
(obj instanceof Object);          // true
(obj instanceof Foo);             // true
(obj.constructor == Foo);         // true
(obj.constructor.name == "Foo");  // true

// let's add some prototypical inheritance
function Bar() { this.b = 2; }
Foo.prototype = new Bar();
obj = new Foo();
(obj instanceof Object);          // true
(obj instanceof Foo);             // true
(obj.constructor == Foo);         // false
(obj.constructor.name == "Foo");  // false


// using an anonymous function:
obj = new (function() { this.a = 1; })();
(obj instanceof Object);              // true
(obj.constructor == obj.constructor); // true
(obj.constructor.name == "");         // true


// using an anonymous function assigned to a variable
var Foo = function() { this.a = 1; };
obj = new Foo();
(obj instanceof Object);      // true
(obj instanceof Foo);         // true
(obj.constructor == Foo);     // true
(obj.constructor.name == ""); // true


// using object literal syntax
obj = { foo : 1 };
(obj instanceof Object);            // true
(obj.constructor == Object);        // true
(obj.constructor.name == "Object"); // true

虽然并非所有的排列都出现在这组例子中,但希望有足够的信息可以让您了解根据您的需求可能会有多乱。不要假设任何东西,如果你不明白你究竟是什么,你可能会因为缺乏细微之处而最终破解你不希望它的代码。

注意:

讨论 typeof 运算符似乎是一个明显的遗漏,但它确实无助于确定是否有用 object 是一种给定的类型,因为它非常简单。了解在哪里 typeof 有用是很重要的,但我目前不认为它与这个讨论非常相关。我的思想可以改变。 :)


1400
2017-12-01 22:21



嗯,我想我也可能 - Stack Overflow的观点有点像维基,我认为这更符合这个意图。无论如何,我只是想要有点彻底。 - Jason Bunting
重复下面的答案---您对Object原型的扩展在IE8中不起作用 - 有谁知道IE8中有什么用? - Adam
如果你像这个函数那样工作a(){this.a = 1;} function b(){this.b = 2; } b.prototype = new a(); // b继承自b.prototype.constructor = b; //正确的原型继承方式var f = new b(); //使用b构造函数创建新对象(f.constructor == b); // TRUE(f.constructor == a); // FALSE - Boris Hamanov
现在,这就是StackOverflow上的大部分答案。 (不要把答案的长度作为定义参数,但要全面) - kumar_harsh
重要的是要注意检查对象的任何技术 constructor 方法(用 .toString() 要么 .name如果您的Javascript已经使用像uglify或Rails资产管道这样的工具缩小,那么将无法工作。缩小重命名构造函数,因此最终会得到不正确的类名 n。如果你在这种情况下,你可能想要 手动 定义一个 className 对象上的属性并使用它。 - Gabe Martin-Dempesy


Jason Bunting的回答给了我足够的线索来找到我需要的东西:

<<Object instance>>.constructor.name

因此,例如,在下面的代码中:

function MyObject() {}
var myInstance = new MyObject();

myInstance.constructor.name 会回来的 "MyObject"


103
2018-06-16 22:25



为了完整起见,值得一提的是,使用constructor.name只有在使用命名函数作为构造函数而不是分配给变量的匿名函数时才有效。 - Matthew Crumley
为了完整起见,值得一提的是它在IE浏览器中不起作用 - 它们不支持函数的“name”属性。 - Eugene Lazutkin
@Eugene - 我忘记了......我想我在浏览器之外花了太多时间做javascript。 - Matthew Crumley
似乎也不适用于FF(v26) - ricosrealm
function getType(o) { return o && o.constructor && o.constructor.name } - justin.m.chase


我用的一个小技巧:

function Square(){
    this.className = "Square";
    this.corners = 4;
}

var MySquare = new Square();
console.log(MySquare.className); // "Square"

24
2017-08-28 15:59



我不是特别喜欢这个。这更像是一种肮脏的伎俩。另一方面,如果你没有太多的构造函数,它可能会工作得很好。 - pimvdb
@pimvdb:我认为它比修改对象的原型更清晰,是接受的答案。 - Daniel Szabo
@DanielSzabo如果一个属性在原型的所有实例之间应该具有相同的值,我肯定更喜欢将它放在原型上 - 将它放在每个实例上都是超冗余的,并且原型本身缺少元数据。也就是说,ES6采用了最明智的解决方案:如果有的话 class Square, 名字是 Square.name / MySquare.constructor.name 而不是 Square.prototype.name;通过推杆 name 在构造函数上,它不会污染原型或任何实例,但可以从任何一个实例访问。 - Andy


更新

确切地说,我认为OP要求一个函数来检索特定对象的构造函数名称。在Javascript方面, object 没有类型但是类型 本身。但是,不同的对象可以有不同 建设者

Object.prototype.getConstructorName = function () {
   var str = (this.prototype ? this.prototype.constructor : this.constructor).toString();
   var cname = str.match(/function\s(\w*)/)[1];
   var aliases = ["", "anonymous", "Anonymous"];
   return aliases.indexOf(cname) > -1 ? "Function" : cname;
}

new Array().getConstructorName();  // returns "Array"
(function () {})().getConstructorName(); // returns "Function"


注意: 以下示例已弃用。

一个 博客文章 由...链接 Christian Sciberras 包含一个如何做的好例子。即,通过扩展Object原型:

if (!Object.prototype.getClassName) {
    Object.prototype.getClassName = function () {
        return Object.prototype.toString.call(this).match(/^\[object\s(.*)\]$/)[1];
    }
}

var test = [1,2,3,4,5];

alert(test.getClassName()); // returns Array

16
2018-03-24 12:26



很好,但我们再次命名:JS没有类。 - mikemaccana
@nailer - 我建议使用更新的功能,较旧的功能仅仅是出于历史原因而保留。 - Saul
这是有效的,但应该注意的是,它可以在不修改Object.prototype的情况下完成,通过创建一个将对象作为第一个参数并在函数内使用它而不是'this'的函数。 - Matt Browne
@Matt - 当然。只是拥有一个对象方法更简洁: test.getClassName() VS getClassName.apply(test)。 - Saul


使用Object.prototype.toString

事实证明,正如这篇文章的详细信息,您可以使用Object.prototype.toString(toString的低级和通用实现)来获取所有内置类型的类型

Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(/abc/) // [object RegExp]
Object.prototype.toString.call([1,2,3]) // [object Array]

人们可以写一个简短的辅助函数,如

function type(obj){
    return Object.prototype.toString.call(obj]).match(/\s\w+/)[0].trim()
}

return [object String] as String
return [object Number] as Number
return [object Object] as Object
return [object Undefined] as Undefined
return [object Function] as Function

9
2018-03-05 14:42



您不需要使用正则表达式来解析对象名称。只是用 .slice(): Object.prototype.toString.call(obj).slice( 8, -1 ); - bobobobo


这是我提出的一个解决方案,它解决了instanceof的缺点。它可以从跨窗口和跨帧检查对象的类型,并且没有原始类型的问题。

function getType(o) {
    return Object.prototype.toString.call(o).match(/^\[object\s(.*)\]$/)[1];
}
function isInstance(obj, type) {
    var ret = false,
    isTypeAString = getType(type) == "String",
    functionConstructor, i, l, typeArray, context;
    if (!isTypeAString && getType(type) != "Function") {
        throw new TypeError("type argument must be a string or function");
    }
    if (obj !== undefined && obj !== null && obj.constructor) {
        //get the Function constructor
        functionConstructor = obj.constructor;
        while (functionConstructor != functionConstructor.constructor) {
            functionConstructor = functionConstructor.constructor;
        }
        //get the object's window
        context = functionConstructor == Function ? self : functionConstructor("return window")();
        //get the constructor for the type
        if (isTypeAString) {
            //type is a string so we'll build the context (window.Array or window.some.Type)
            for (typeArray = type.split("."), i = 0, l = typeArray.length; i < l && context; i++) {
                context = context[typeArray[i]];
            }
        } else {
            //type is a function so execute the function passing in the object's window
            //the return should be a constructor
            context = type(context);
        }
        //check if the object is an instance of the constructor
        if (context) {
            ret = obj instanceof context;
            if (!ret && (type == "Number" || type == "String" || type == "Boolean")) {
                ret = obj.constructor == context
            }
        }
    }
    return ret;
}

isInstance需要两个参数:一个对象和一个类型。它如何工作的真正技巧是它检查对象是否来自同一窗口,如果没有获取对象的窗口。

例子:

isInstance([], "Array"); //true
isInstance("some string", "String"); //true
isInstance(new Object(), "Object"); //true

function Animal() {}
function Dog() {}
Dog.prototype = new Animal();

isInstance(new Dog(), "Dog"); //true
isInstance(new Dog(), "Animal"); //true
isInstance(new Dog(), "Object"); //true
isInstance(new Animal(), "Dog"); //false

type参数也可以是一个返回构造函数的回调函数。回调函数将接收一个参数,该参数是所提供对象的窗口。

例子:

//"Arguments" type check
var args = (function() {
    return arguments;
}());

isInstance(args, function(w) {
    return w.Function("return arguments.constructor")();
}); //true

//"NodeList" type check
var nl = document.getElementsByTagName("*");

isInstance(nl, function(w) {
    return w.document.getElementsByTagName("bs").constructor;
}); //true

要记住的一件事是IE <9不提供所有对象的构造函数,因此NodeList的上述测试将返回false,并且isInstance(alert,“Function”)将返回false。


8
2017-08-12 19:38





使用 constructor.name 什么时候可以,当我不能时,正则表达式功能。

Function.prototype.getName = function(){
  if (typeof this.name != 'undefined')
    return this.name;
  else
    return /function (.+)\(/.exec(this.toString())[1];
};

6
2017-10-23 15:04