关于oop:如何获取javascript对象的类?

How to get a JavaScript object's class?

我创建了一个javascript对象,但是如何确定该对象的类呢?

我想要类似Java的EDCOX1×0的方法。


在JavaScript中,Java的EDCOX1与0不存在精确的对应关系。这主要是因为JavaScript是基于原型的语言,而不是Java是基于类的语言。

根据您所需的getClass(),javascript中有几个选项:

  • typeof
  • instanceof
  • 江户十一〔4〕江户十一〔5〕号
  • func.prototypeprotoisPrototypeOf

举几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Foo() {}
var foo = new Foo();

typeof Foo;             // =="function"
typeof foo;             // =="object"

foo instanceof Foo;     // == true
foo.constructor.name;   // =="Foo"
Foo.name                // =="Foo"    

Foo.prototype.isPrototypeOf(foo);   // == true

Foo.prototype.bar = function (x) {return x+x;};
foo.bar(21);            // == 42

注意:如果使用uglify编译代码,它将更改非全局类名。为了防止这种情况发生,Uglify有一个--mangle参数,可以设置为false,即使用gulp或grunt。


1
obj.constructor.name

在现代浏览器中是一种可靠的方法。在ES6中,官方将Function.name添加到标准中,使其成为一种标准兼容的方法,将javascript对象的"类"作为字符串。如果对象是用var obj = new MyClass()实例化的,它将返回"myclass"。

它将为数字返回"数字",为数组返回"数组",为函数返回"函数"等。它通常按预期工作。唯一失败的情况是,如果一个对象是在没有原型的情况下通过Object.create( null )创建的,或者该对象是通过匿名定义(未命名)函数实例化的。

还要注意,如果您正在缩小代码,那么与硬编码类型字符串进行比较是不安全的。例如,不检查obj.constructor.name =="MyType",而是检查obj.constructor.name == MyType.name。或者只是比较构造函数本身,但是这不会跨越DOM边界,因为每个DOM上都有不同的构造函数函数实例,因此对其构造函数进行对象比较是行不通的。

链接原型时注意

奇怪的是,constructor.name返回了原型链中使用的最基本函数的名称,这是不直观的。例如,如果B原型地从A派生,并且您创建了BBb.constructor.name的新实例,则返回"a",这似乎是错误的。但是,对于单级原型和所有原语来说,它确实可以很好地工作。


此函数从Object.prototype.toString.call(someObject)返回"undefined""null"[object class]中的"class"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getClass(obj) {
  if (typeof obj ==="undefined")
    return"undefined";
  if (obj === null)
    return"null";
  return Object.prototype.toString.call(obj)
    .match(/^\[object\s(.*)\]$/)[1];
}

getClass("")   ==="String";
getClass(true) ==="Boolean";
getClass(0)    ==="Number";
getClass([])   ==="Array";
getClass({})   ==="Object";
getClass(null) ==="null";
// etc...


要获得"伪类",可以通过

1
obj.constructor

假设当您进行继承时,constructor设置正确,这是通过如下方式实现的:

1
2
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

这两条线,加上:

1
var woofie = new Dog()

将使woofie.constructor指向Dog。注意,Dog是一个构造函数函数,是一个Function对象。但你可以做if (woofie.constructor === Dog) { ... }

如果你想得到一个字符串形式的类名,我发现以下方法很有效:

http://blog.magniq.com/post/514962277/finding-out-class-name-of-javascript-objects

1
2
3
4
5
6
7
8
9
10
11
12
function getObjectClass(obj) {
    if (obj && obj.constructor && obj.constructor.toString) {
        var arr = obj.constructor.toString().match(
            /function\s*(\w+)/);

        if (arr && arr.length == 2) {
            return arr[1];
        }
    }

    return undefined;
}

它获取构造函数函数,将其转换为字符串,并提取构造函数函数的名称。

请注意,obj.constructor.name本可以工作得很好,但它不是标准的。它在Chrome和Firefox上,但不在IE上,包括IE9或IE10RTM。


您可以使用constructor属性获取对创建对象的constructor函数的引用:

1
2
3
4
5
function MyObject(){
}

var obj = new MyObject();
obj.constructor; // MyObject

如果需要在运行时确认对象的类型,可以使用instanceof运算符:

1
obj instanceof MyObject // true


我现在有一个通用的工作环境,并使用了这个:

1
2
3
4
5
6
7
8
9
class Test {
  // your class definition
}

nameByType = function(type){
  return type.prototype["constructor"]["name"];
};

console.log(nameByType(Test));

如果没有对象的实例,这是我找到的通过类型输入获取类名的唯一方法。

(ES2017)

点记法也很好用

1
console.log(Test.prototype.constructor.name); // returns"Test"


为了保持其向后兼容性(ecmascript 6)的完整记录,javascript仍然没有class类型(尽管并非每个人都理解这一点)。它确实有一个class关键字作为创建原型的class语法的一部分,但仍然没有所谓的类。JavaScript现在不是,也从来不是经典的OOP语言。从类的角度来说,使用JS只是一种误导,或者是一种尚未探索原型继承(只是保持它的真实性)的迹象。

这意味着this.constructor仍然是获取constructor函数引用的一个很好的方法。this.constructor.prototype是访问原型本身的方法。因为这不是Java,它不是一个类。它是实例实例化的原型对象。下面是一个使用ES6语法糖创建原型链的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Foo {
  get foo () {
    console.info(this.constructor, this.constructor.name)
    return 'foo'
  }
}

class Bar extends Foo {
  get foo () {
    console.info('[THIS]', this.constructor, this.constructor.name, Object.getOwnPropertyNames(this.constructor.prototype))
    console.info('[SUPER]', super.constructor, super.constructor.name, Object.getOwnPropertyNames(super.constructor.prototype))

    return `${super.foo} + bar`
  }
}

const bar = new Bar()
console.dir(bar.foo)

这就是使用babel-node输出的结果:

1
2
3
4
5
> $ babel-node ./foo.js                                                                                                                   ? 6.2.0 [±master ●]
[THIS] [Function: Bar] 'Bar' [ 'constructor', 'foo' ]
[SUPER] [Function: Foo] 'Foo' [ 'constructor', 'foo' ]
[Function: Bar] 'Bar'
'foo + bar'

给你!2016年,在javascript中有一个class关键字,但仍然没有类类型。this.constructor是获取构造函数函数的最佳方法,this.constructor.prototype是获取原型本身的最佳方法。


对于ES6中的javascript类,可以使用object.constructor。在getClass()方法下面的示例类中,按预期返回es6类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var Cat = class {

    meow() {

        console.log("meow!");

    }

    getClass() {

        return this.constructor;

    }

}

var fluffy = new Cat();

...

var AlsoCat = fluffy.getClass();
var ruffles = new AlsoCat();

ruffles.meow();    //"meow!"

如果从getClass方法实例化类,请确保将其包装在括号中,例如ruffles = new ( fluffy.getClass() )( args... );中。


我发现object.constructor.toString()在ie中返回[object objectClass],而不是在chome中返回function objectClass () {}。因此,我认为http://blog.magniq.com/post/514962277/finding-out-class-names-of-javascript-objects中的代码在IE中可能无法正常工作,我修复了以下代码:

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var getObjectClass = function (obj) {
        if (obj && obj.constructor && obj.constructor.toString()) {

                /*
                 *  for browsers which have name property in the constructor
                 *  of the object,such as chrome
                 */

                if(obj.constructor.name) {
                    return obj.constructor.name;
                }
                var str = obj.constructor.toString();
                /*
                 * executed if the return of object.constructor.toString() is
                 *"[object objectClass]"
                 */


                if(str.charAt(0) == '[')
                {
                        var arr = str.match(/\[\w+\s*(\w+)\]/);
                } else {
                        /*
                         * executed if the return of object.constructor.toString() is
                         *"function objectClass () {}"
                         * for IE Firefox
                         */

                        var arr = str.match(/function\s*(\w+)/);
                }
                if (arr && arr.length == 2) {
                            return arr[1];
                        }
          }
          return undefined;
    };

在JavaScript中,没有类,但我认为您需要构造函数名,obj.constructor.toString()将告诉您需要什么。


同意DFA,这就是为什么当没有找到命名类时,我将原型视为类的原因。

这里是一个升级功能的一个张贴的伊莱格雷,以符合我的想法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function what(obj){
    if(typeof(obj)==="undefined")return"undefined";
    if(obj===null)return"Null";
    var res = Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1];
    if(res==="Object"){
        res = obj.constructor.name;
        if(typeof(res)!='string' || res.length==0){
            if(obj instanceof jQuery)return"jQuery";// jQuery build stranges Objects
            if(obj instanceof Array)return"Array";// Array prototype is very sneaky
            return"Object";
        }
    }
    return res;
}


尝试我的库类型脚本类帮助程序:

1
2
3
4
5
6
7
8
9
10
import { CLASS } from 'typescript-class-helpers'

@CLASS.NAME('Example')
class Example {

}

console.log(CLASS.getName(Example) === 'Example') // true
console.log(CLASS.getNameFromObject(new Example()) === 'Example') // true
console.log(CLASS.getBy('Example') === Example) // true

通过这种方式,您可以使用类名,即使在不合格的过程之后。

安装:

1
npm i typescript-class-helpers

JavaScript是一种无类语言:没有类像Java一样静态地定义一个类的行为。JavaScript使用原型而不是类来定义对象属性,包括方法和继承。可以用JavaScript中的原型来模拟许多基于类的特性。


下面是getClass()getInstance()的实现

您可以使用window获得对象类的引用。

从实例上下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function A() {
    this.getClass = function() {
        return window[this.constructor.name];
    }

    this.getNewInstance = function() {
        return new window[this.constructor.name];
    }
}

var a = new A();
console.log(a.getClass());  //  function A { // etc... }

// you can even:
var b = new a.getClass();
b instanceof A; // true

从静态上下文:

1
2
3
4
5
6
7
8
9
function B() {};

B.getClass = function() {
    return window[this.name];
}

B.getInstance() {
    return new window[this.name];
}


问题似乎已经解决了,但是OP想要访问类和对象,就像我们在Java中所做的那样,所选择的答案是不够的(IMHO)。

通过下面的解释,我们可以得到一个对象类(它实际上在javascript中被称为原型)。

1
2
var arr = new Array('red', 'green', 'blue');
var arr2 = new Array('white', 'black', 'orange');

可以添加这样的属性:

1
2
3
4
5
6
Object.defineProperty(arr,'last', {
  get: function(){
    return this[this.length -1];
  }
});
console.log(arr.last) // blue

.last属性只能用于从数组原型实例化的'arr对象。因此,为了使从数组原型实例化的所有对象都可以使用.last属性,我们必须为数组原型定义.last属性:

1
2
3
4
5
6
7
Object.defineProperty(Array.prototype,'last', {
  get: function(){
    return this[this.length -1];
  }
});
console.log(arr.last) // blue
console.log(arr2.last) // orange

这里的问题是,您必须知道"EDOCX1"(18)和"EDOCX1"(22)变量属于哪个对象类型(原型)!换句话说,如果您不知道"EDOCX1"(18)对象的类类型(原型),那么就无法为它们定义属性。在上面的例子中,我们知道arr是array对象的实例,这就是为什么我们使用array.prototype来定义array的属性。但是,如果我们不知道"EDOCX1"(18)的类(原型)怎么办?

1
2
3
4
5
6
7
Object.defineProperty(arr.__proto__,'last2', {
  get: function(){
    return this[this.length -1];
  }
});
console.log(arr.last) // blue
console.log(arr2.last) // orange

如您所见,在不知道"EDOCX1"(18)是数组的情况下,我们可以通过使用"EDOCX1"(27)添加一个新属性,只需引用"EDOCX1"(18)的类即可。

我们访问了"EDOCX1"(18)的原型,但不知道它是数组的一个实例,我认为这就是OP要求的。