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
相關文章
相關標籤/搜索