JavaScript中Object.prototype.toString方法的原理

  在JavaScript中,想要判断某个对象值属于哪一种内置类型,最靠谱的作法就是经过Object.prototype.toString方法.浏览器

  <一>, ECMAScript 3 函数

  1. 在ES3中,Object.prototype.toString方法的规范以下:工具

15.2.4.2 Object.prototype.toString()

  在toString方法被调用时,会执行下面的操做步骤:测试

  1. 获取this对象的[[Class]]属性的值.this

  2. 计算出三个字符串"[object ", 第一步的操做结果Result(1), 以及 "]"链接后的新字符串.spa

  3. 返回第二步的操做结果Result(2).prototype

  [[Class]]是一个内部属性,全部的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的code

  备注: 而后给了一段解释对象

  全部内置对象的[[Class]]属性的值是由本规范定义的.全部宿主对象的[[Class]]属性的值能够是任意值,甚至能够是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值能够用来判断一个原生对象属于哪一种内置类型.须要注意的是,除了经过Object.prototype.toString方法以外,本规范没有提供任何其余方式来让程序访问该属性的值(查看 15.2.4.2).blog

  也就是说,把Object.prototype.toString方法返回的字符串,去掉前面固定的"[object "和后面固定的"]",就是内部属性[[class]]的值,也就达到了判断对象类型的目的.jQuery中的工具方法$.type(),就是干这个的.

  在ES3中,规范文档并无总结出[[class]]内部属性一共有几种,不过咱们能够本身统计一下,原生对象的[[class]]内部属性的值一共有10种.分别是:"Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String".

  <二>, ECMAScript 5

  在ES5.1中,除了规范写的更详细一些之外,Object.prototype.toString方法和[[class]]内部属性的定义上也有一些变化,Object.prototype.toString方法的规范以下:

  在toString方法被调用时,会执行下面的操做步骤:

    若是this的值为undefined,则返回"[object Undefined]".

    若是this的值为null,则返回"[object Null]".

    让O成为调用ToObject(this)的结果.

    让class成为O的内部属性[[Class]]的值.

  返回三个字符串"[object ", class, 以及 "]"链接后的新字符串.

  能够看出,比ES3多了1,2,3步.第1,2步属于新规则,比较特殊,由于"Undefined"和"Null"并不属于[[class]]属性的值,须要注意的是,这里和严格模式无关(大部分函数在严格模式下,this的值才会保持undefined或null,非严格模式下会自动成为全局对象).第3步并不算是新规则,由于在ES3的引擎中,也都会在这一步将三种原始值类型转换成对应的包装对象,只是规范中没写出来.ES5中,[[Class]]属性的解释更加详细:

  全部内置对象的[[Class]]属性的值是由本规范定义的.全部宿主对象的[[Class]]属性的值能够是除了"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"以外的的任何字符串.[[Class]]内部属性是引擎内部用来判断一个对象属于哪一种类型的值的.须要注意的是,除了经过Object.prototype.toString方法以外,本规范没有提供任何其余方式来让程序访问该属性的值(查看 15.2.4.2).

  和ES3对比一下,第一个差异就是[[class]]内部属性的值多了两种,成了12种,一种是arguments对象的[[class]]成了"Arguments",而不是之前的"Object",还有就是多个了全局对象JSON,它的[[class]]值为"JSON".第二个差异就是,宿主对象的[[class]]内部属性的值,不能和这12种值冲突,不过在支持ES3的浏览器中,貌似也没有发现哪些宿主对象故意使用那10个值.

  <三>, ECMAScript 5

  ES6中 @@toStringTag很容易改变Object.prototype.toString返回值

let obj = {}

Object.defineProperty(obj, Symbol.toStringTag, {
    get: function() {
        return "newClass"
    }
})

console.log(Object.prototype.toString.call(obj)) // "[object newClass]"

   <四>, 总结

  若是咱们要判断一个某个对象的内置类型,能够用以下函数

function getClass (a) {
    const str = Object.prototype.toString.call(a);
    return /^\[object (.*)\]$/.exec(str)[1].toLocaleUpperCase();
}

  测试以下

   function foo() {
        console.log(getClass(arguments)); // ARGUMENTS
    }
    foo();
    function* nav() {
        console.log(getClass(arguments)); // ARGUMENTS
        yield 1;
        yield 2;
    }
    let its = nav();
    its.next();
    console.log(getClass({})); // OBJECT
    console.log(getClass([])); // ARRAY
    console.log(getClass(1234)); // NUMBER
    console.log(getClass("wuhan")); // STRING
    console.log(getClass(/^[a-z]+/gi)); // REGEXP
    console.log(getClass(true)); // BOOLEAN
    console.log(getClass(new Date())); // DATE
    console.log(getClass(new Error())); // ERROR
    console.log(getClass(new Function())); // FUNCTION
    console.log(getClass(Math)); // MATH
    console.log(getClass(null)); // NULL
    console.log(getClass(undefined)); // UNDEFINED
相关文章
相关标签/搜索