为什么Object.protype.toString.call(foo)可以检测到foo's类型

Why can Object.prototype.toString.call(foo) detect foo's type?

本文关键字:foo 检测 类型 protype Object toString call 为什么      更新时间:2023-09-26

我知道我们可以在Javascript中检测变量的类型,如下所示:

Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call({}); // [object Object]
Object.prototype.toString.call(''); // [object String]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(function () {}); // [object Function]
Object.prototype.toString.call(/test/i); // [object RegExp]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(); // [object Undefined]

但为什么呢?

这些值([object Array]、[object String]…)是如何返回的,Object.prototype.toString做什么?

Object.prototype.toString基本上返回对象的[[Class]](实现细节)内部属性。引用ECMA Script 5.1规范中的一节,其中定义了

  1. 如果this的值为undefined,则返回"[object Undefined]"
  2. 如果this的值为null,则返回"[object Null]"
  3. O是调用ToObject并将this值作为参数的结果
  4. classO[[Class]]内部属性的值
  5. 返回字符串值,该值是连接三个字符串"[object "class"]"的结果

此外,

[[Class]]内部属性的值由本规范为每种内置对象定义。主机对象的[[Class]]内部属性的值可以是除了"Arguments""Array""Boolean""Date""Error""Function""JSON""Math""Number""Object""RegExp""String"中的一个之外的任何字符串值。[[Class]]内部属性的值在内部用于区分不同类型的对象请注意,此规范没有为程序提供任何访问该值的方法,除非通过Object.prototype.toString

因此,Object.prototype.toString是唯一可以访问[[Class]]属性的函数。

如果你碰巧使用ECMA 6(就像在NodeJS或更新的浏览器技术上一样),你可以使用以下函数来获取"类名"。

// primitives
var array = [], obj = {}, str = "", date = new Date(), 
            num = 1, flt = 1.0, reg = new RegExp(/woohoo/g), 
            bool = true, myType = new MyType(), udf = undefined, nul = null;
// names of various types (primitive and not)
var names = cName(array) + ", " + cName(obj) + ", " + cName(str) + ", " +
            cName(num) + ", " + cName(flt) + ", " + cName(reg) + ", " + 
            cName(bool) + ", " +  cName(date) + ", " + cName(myType) + ", " + 
            cName(MyType) + ", " +  cName(udf) + ", " + cName(nul);
// custom type
function MyType(){}
console.log( names ); 
// output: 
// Array, Object, String, Number, Number, RegExp, Boolean, Date, MyType, MyType, undefined, null
// implementation
function cName(obj){
    // default to non-null value.
    var ret = '';
    if(typeof obj === 'undefined')  { ret = 'undefined';  }
    else if(obj === null)           { ret = String(obj); }
    else if(typeof obj.constructor !== 'undefined' && obj.constructor !== null){
        ret = obj.constructor.name
        if(ret == 'Function')       { ret = obj.name; }
    }
    return ret;
}

虽然确实没有"类",但当传递类似Array, vs Object, vs. Null的东西时,如果你想知道它是哪一个,这会有所帮助。

在其中任何一个上调用CCD_ 35将返回CCD_。然后是必须处理nullundefined之类的事情的警告。

调用Object.prototype.toString()比访问constructor.name更重,因为没有从某种类型到字符串的转换,我相信constructorconstructor.name都是成员变量,而不是getter,这意味着在检索所述名称时没有调用其他函数。