在JavaScript中,想要判斷某個對象值屬於哪一種內置類型,最靠譜的作法就是經過Object.prototype.toString方法.瀏覽器
1
2
|
var
arr = [];
console.log(Object.prototype.toString.call(arr))
//"[object Array]"
|
本文要講的就是,toString方法是如何作到這一點的,原理是什麼.app
ECMAScript 3函數
在ES3中,Object.prototype.toString方法的規範以下:工具
1
|
15.2.4.2 Object.prototype.toString()
|
在toString方法被調用時,會執行下面的操做步驟:this
1. 獲取this對象的[[Class]]屬性的值.spa
2. 計算出三個字符串"[object ", 第一步的操做結果Result(1), 以及 "]"鏈接後的新字符串..net
3. 返回第二步的操做結果Result(2).prototype
[[Class]]是一個內部屬性,全部的對象(原生對象和宿主對象)都擁有該屬性.在規範中,[[Class]]是這麼定義的code
內部屬性 | 描述 |
---|---|
[[Class]] | 一個字符串值,代表了該對象的類型. |
而後給了一段解釋:orm
全部內置對象的[[Class]]屬性的值是由本規範定義的.全部宿主對象的[[Class]]屬性的值能夠是任意值,甚至能夠是內置對象使用過的[[Class]]屬性的值.[[Class]]屬性的值能夠用來判斷一個原生對象屬於哪一種內置類型.須要注意的是,除了經過Object.prototype.toString方法以外,本規範沒有提供任何其餘方式來讓程序訪問該屬性的值(查看 15.2.4.2).
也就是說,把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方法的規範以下:
15.2.4.2 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 6
ES6目前還只是工做草案,但可以確定的是,[[class]]內部屬性沒有了,取而代之的是另一個內部屬性[[NativeBrand]].[[NativeBrand]]屬性是這麼定義的:
內部屬性 | 屬性值 | 描述 |
---|---|---|
[[NativeBrand]] | 枚舉NativeBrand的一個成員. | 該屬性的值對應一個標誌值(tag value),能夠用來區分原生對象的類型. |
[[NativeBrand]]屬性的解釋:
[[NativeBrand]]內部屬性用來識別某個原生對象是否爲符合本規範的某一種特定類型的對象.[[NativeBrand]]內部屬性的值爲下面這些枚舉類型的值中的一個:NativeFunction, NativeArray, StringWrapper, BooleanWrapper, NumberWrapper, NativeMath, NativeDate, NativeRegExp, NativeError, NativeJSON, NativeArguments, NativePrivateName.[[NativeBrand]]內部屬性僅用來區分區分特定類型的ECMAScript原生對象.只有在表10中明確指出的對象類型纔有[[NativeBrand]]內部屬性.
表10 — [[NativeBrand]]內部屬性的值
屬性值 | 對應類型 |
---|---|
NativeFunction | Function objects |
NativeArray | Array objects |
StringWrapper | String objects |
BooleanWrapper | Boolean objects |
NumberWrapper | Number objects |
NativeMath | The Math object |
NativeDate | Date objects |
NativeRegExp | RegExp objects |
NativeError | Error objects |
NativeJSON | The JSON object |
NativeArguments | Arguments objects |
NativePrivateName | Private Name objects |
可見,和[[class]]不一樣的是,並非每一個對象都擁有[[NativeBrand]].同時,Object.prototype.toString方法的規範也改爲了下面這樣:
15.2.4.2 Object.prototype.toString ( )
在toString方法被調用時,會執行下面的操做步驟:
若是this的值爲undefined,則返回"[object Undefined]".
若是this的值爲null,則返回"[object Null]".
讓O成爲調用ToObject(this)的結果.
若是O有[[NativeBrand]]內部屬性,讓tag成爲表29中對應的值.
不然
讓hasTag成爲調用O的[[HasProperty]]內部方法後的結果,參數爲@@toStringTag.
若是hasTag爲false,則讓tag爲"Object".
不然,
讓tag成爲調用O的[[Get]]內部方法後的結果,參數爲@@toStringTag.
若是tag是一個abrupt completion,則讓tag成爲NormalCompletion("???").
讓tag成爲tag.[[value]].
若是Type(tag)不是字符串,則讓tag成爲"???".
若是tag的值爲"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp",或
者"String"中的任一個,則讓tag成爲字符串"~"和tag當前的值鏈接後的結果.
返回三個字符串"[object ", tag, and "]"鏈接後的新字符串.
表29 — [[NativeBrand]] 標誌值
[[NativeBrand]]值 | 標誌值 |
---|---|
NativeFunction | "Function" |
NativeArray | "Array" |
StringWrapper | "String" |
BooleanWrapper | "Boolean" |
NumberWrapper | "Number" |
NativeMath | "Math" |
NativeDate | "Date" |
NativeRegExp | "RegExp" |
NativeError | "Error" |
NativeJSON | "JSON" |
NativeArguments | "Arguments" |
能夠看到,在規範上有了很大的變化,不過對於普通用戶來講,貌似感受不到.
也許你發現了,ES6裏的新類型Map,Set等,都沒有在表29中.它們在執行toString方法的時候返回的是什麼?
1
2
|
console.log(Object.prototype.toString.call(Map()))
//"[object Map]"
console.log(Object.prototype.toString.call(Set()))
//"[object Set]"
|
其中的字符串"Map"是怎麼來的呢:
15.14.5.13 Map.prototype.@@toStringTag
@@toStringTag 屬性的初始值爲字符串"Map".
因爲ES6的規範還在制定中,各類相關規定都有可能改變,因此若是想了解更多細節.看看下面這兩個連接,如今只須要知道的是:[[class]]沒了,使用了更復雜的機制.
以上所述是小編給你們分享的JavaScript中Object.prototype.toString方法的原理,但願對你們有所幫助!