JavaScript中Object.prototype.toString方法的原理
在JavaScript中,想要判斷某個(gè)對(duì)象值屬于哪種內(nèi)置類型,最靠譜的做法就是通過(guò)Object.prototype.toString方法.
var arr = []; console.log(Object.prototype.toString.call(arr)) //"[object Array]"
本文要講的就是,toString方法是如何做到這一點(diǎn)的,原理是什么.
ECMAScript 3
在ES3中,Object.prototype.toString方法的規(guī)范如下:
15.2.4.2 Object.prototype.toString()
在toString方法被調(diào)用時(shí),會(huì)執(zhí)行下面的操作步驟:
1. 獲取this對(duì)象的[[Class]]屬性的值.
2. 計(jì)算出三個(gè)字符串"[object ", 第一步的操作結(jié)果Result(1), 以及 "]"連接后的新字符串.
3. 返回第二步的操作結(jié)果Result(2).
[[Class]]是一個(gè)內(nèi)部屬性,所有的對(duì)象(原生對(duì)象和宿主對(duì)象)都擁有該屬性.在規(guī)范中,[[Class]]是這么定義的
內(nèi)部屬性 | 描述 |
---|---|
[[Class]] | 一個(gè)字符串值,表明了該對(duì)象的類型. |
然后給了一段解釋:
所有內(nèi)置對(duì)象的[[Class]]屬性的值是由本規(guī)范定義的.所有宿主對(duì)象的[[Class]]屬性的值可以是任意值,甚至可以是內(nèi)置對(duì)象使用過(guò)的[[Class]]屬性的值.[[Class]]屬性的值可以用來(lái)判斷一個(gè)原生對(duì)象屬于哪種內(nèi)置類型.需要注意的是,除了通過(guò)Object.prototype.toString方法之外,本規(guī)范沒(méi)有提供任何其他方式來(lái)讓程序訪問(wèn)該屬性的值(查看 15.2.4.2).
也就是說(shuō),把Object.prototype.toString方法返回的字符串,去掉前面固定的"[object "和后面固定的"]",就是內(nèi)部屬性[[class]]的值,也就達(dá)到了判斷對(duì)象類型的目的.jQuery中的工具方法$.type(),就是干這個(gè)的.
在ES3中,規(guī)范文檔并沒(méi)有總結(jié)出[[class]]內(nèi)部屬性一共有幾種,不過(guò)我們可以自己統(tǒng)計(jì)一下,原生對(duì)象的[[class]]內(nèi)部屬性的值一共有10種.分別是:"Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String".
ECMAScript 5
在ES5.1中,除了規(guī)范寫的更詳細(xì)一些以外,Object.prototype.toString方法和[[class]]內(nèi)部屬性的定義上也有一些變化,Object.prototype.toString方法的規(guī)范如下:
15.2.4.2 Object.prototype.toString ( )
在toString方法被調(diào)用時(shí),會(huì)執(zhí)行下面的操作步驟:
如果this的值為undefined,則返回"[object Undefined]".
如果this的值為null,則返回"[object Null]".
讓O成為調(diào)用ToObject(this)的結(jié)果.
讓class成為O的內(nèi)部屬性[[Class]]的值.
返回三個(gè)字符串"[object ", class, 以及 "]"連接后的新字符串.
可以看出,比ES3多了1,2,3步.第1,2步屬于新規(guī)則,比較特殊,因?yàn)?Undefined"和"Null"并不屬于[[class]]屬性的值,需要注意的是,這里和嚴(yán)格模式無(wú)關(guān)(大部分函數(shù)在嚴(yán)格模式下,this的值才會(huì)保持undefined或null,非嚴(yán)格模式下會(huì)自動(dòng)成為全局對(duì)象).第3步并不算是新規(guī)則,因?yàn)樵贓S3的引擎中,也都會(huì)在這一步將三種原始值類型轉(zhuǎn)換成對(duì)應(yīng)的包裝對(duì)象,只是規(guī)范中沒(méi)寫出來(lái).ES5中,[[Class]]屬性的解釋更加詳細(xì):
所有內(nèi)置對(duì)象的[[Class]]屬性的值是由本規(guī)范定義的.所有宿主對(duì)象的[[Class]]屬性的值可以是除了"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"之外的的任何字符串.[[Class]]內(nèi)部屬性是引擎內(nèi)部用來(lái)判斷一個(gè)對(duì)象屬于哪種類型的值的.需要注意的是,除了通過(guò)Object.prototype.toString方法之外,本規(guī)范沒(méi)有提供任何其他方式來(lái)讓程序訪問(wèn)該屬性的值(查看 15.2.4.2).
和ES3對(duì)比一下,第一個(gè)差別就是[[class]]內(nèi)部屬性的值多了兩種,成了12種,一種是arguments對(duì)象的[[class]]成了"Arguments",而不是以前的"Object",還有就是多個(gè)了全局對(duì)象JSON,它的[[class]]值為"JSON".第二個(gè)差別就是,宿主對(duì)象的[[class]]內(nèi)部屬性的值,不能和這12種值沖突,不過(guò)在支持ES3的瀏覽器中,貌似也沒(méi)有發(fā)現(xiàn)哪些宿主對(duì)象故意使用那10個(gè)值.
ECMAScript 6
ES6目前還只是工作草案,但能夠肯定的是,[[class]]內(nèi)部屬性沒(méi)有了,取而代之的是另外一個(gè)內(nèi)部屬性[[NativeBrand]].[[NativeBrand]]屬性是這么定義的:
內(nèi)部屬性 | 屬性值 | 描述 |
---|---|---|
[[NativeBrand]] | 枚舉NativeBrand的一個(gè)成員. | 該屬性的值對(duì)應(yīng)一個(gè)標(biāo)志值(tag value),可以用來(lái)區(qū)分原生對(duì)象的類型. |
[[NativeBrand]]屬性的解釋:
[[NativeBrand]]內(nèi)部屬性用來(lái)識(shí)別某個(gè)原生對(duì)象是否為符合本規(guī)范的某一種特定類型的對(duì)象.[[NativeBrand]]內(nèi)部屬性的值為下面這些枚舉類型的值中的一個(gè):NativeFunction, NativeArray, StringWrapper, BooleanWrapper, NumberWrapper, NativeMath, NativeDate, NativeRegExp, NativeError, NativeJSON, NativeArguments, NativePrivateName.[[NativeBrand]]內(nèi)部屬性僅用來(lái)區(qū)分區(qū)分特定類型的ECMAScript原生對(duì)象.只有在表10中明確指出的對(duì)象類型才有[[NativeBrand]]內(nèi)部屬性.
表10 — [[NativeBrand]]內(nèi)部屬性的值
屬性值 | 對(duì)應(yīng)類型 |
---|---|
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 |
可見(jiàn),和[[class]]不同的是,并不是每個(gè)對(duì)象都擁有[[NativeBrand]].同時(shí),Object.prototype.toString方法的規(guī)范也改成了下面這樣:
15.2.4.2 Object.prototype.toString ( )
在toString方法被調(diào)用時(shí),會(huì)執(zhí)行下面的操作步驟:
如果this的值為undefined,則返回"[object Undefined]".
如果this的值為null,則返回"[object Null]".
讓O成為調(diào)用ToObject(this)的結(jié)果.
如果O有[[NativeBrand]]內(nèi)部屬性,讓tag成為表29中對(duì)應(yīng)的值.
否則
讓hasTag成為調(diào)用O的[[HasProperty]]內(nèi)部方法后的結(jié)果,參數(shù)為@@toStringTag.
如果hasTag為false,則讓tag為"Object".
否則,
讓tag成為調(diào)用O的[[Get]]內(nèi)部方法后的結(jié)果,參數(shù)為@@toStringTag.
如果tag是一個(gè)abrupt completion,則讓tag成為NormalCompletion("???").
讓tag成為tag.[[value]].
如果Type(tag)不是字符串,則讓tag成為"???".
如果tag的值為"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp",或
者"String"中的任一個(gè),則讓tag成為字符串"~"和tag當(dāng)前的值連接后的結(jié)果.
返回三個(gè)字符串"[object ", tag, and "]"連接后的新字符串.
表29 — [[NativeBrand]] 標(biāo)志值
[[NativeBrand]]值 | 標(biāo)志值 |
---|---|
NativeFunction | "Function" |
NativeArray | "Array" |
StringWrapper | "String" |
BooleanWrapper | "Boolean" |
NumberWrapper | "Number" |
NativeMath | "Math" |
NativeDate | "Date" |
NativeRegExp | "RegExp" |
NativeError | "Error" |
NativeJSON | "JSON" |
NativeArguments | "Arguments" |
可以看到,在規(guī)范上有了很大的變化,不過(guò)對(duì)于普通用戶來(lái)說(shuō),貌似感覺(jué)不到.
也許你發(fā)現(xiàn)了,ES6里的新類型Map,Set等,都沒(méi)有在表29中.它們?cè)趫?zhí)行toString方法的時(shí)候返回的是什么?
console.log(Object.prototype.toString.call(Map())) //"[object Map]" console.log(Object.prototype.toString.call(Set())) //"[object Set]"
其中的字符串"Map"是怎么來(lái)的呢:
15.14.5.13 Map.prototype.@@toStringTag
@@toStringTag 屬性的初始值為字符串"Map".
由于ES6的規(guī)范還在制定中,各種相關(guān)規(guī)定都有可能改變,所以如果想了解更多細(xì)節(jié).看看下面這兩個(gè)鏈接,現(xiàn)在只需要知道的是:[[class]]沒(méi)了,使用了更復(fù)雜的機(jī)制.
以上所述是小編給大家分享的JavaScript中Object.prototype.toString方法的原理,希望對(duì)大家有所幫助!
相關(guān)文章
JavaScript 實(shí)現(xiàn)下雪特效的示例代碼
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)下雪特效的示例代碼,幫助大家利用JavaScript制作特效,感興趣的朋友可以了解下2020-09-09JavaScript null和undefined區(qū)別分析
在JavaScript開(kāi)發(fā)中,被人問(wèn)到:null與undefined到底有啥區(qū)別?2009-10-10用IE重起計(jì)算機(jī)或者關(guān)機(jī)的示例代碼
本篇文章主要是對(duì)用IE重起計(jì)算機(jī)或者關(guān)機(jī)的實(shí)現(xiàn)代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-03-03JavaScript求一個(gè)數(shù)組中重復(fù)出現(xiàn)次數(shù)最多的元素及其下標(biāo)位置示例
這篇文章主要介紹了JavaScript求一個(gè)數(shù)組中重復(fù)出現(xiàn)次數(shù)最多的元素及其下標(biāo)位置,涉及javascript數(shù)組元素遍歷、判斷、正則過(guò)濾、追加等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07js實(shí)時(shí)監(jiān)控文本框輸入字?jǐn)?shù)的實(shí)例代碼
下面小編就為大家分享一篇實(shí)時(shí)監(jiān)控文本框輸入字?jǐn)?shù)的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-015秒后跳轉(zhuǎn)到另一個(gè)頁(yè)面的js代碼
跳轉(zhuǎn)到另一個(gè)頁(yè)面的方法有很多,在本文將為大家詳細(xì)介紹下js中如何實(shí)現(xiàn)5秒后跳轉(zhuǎn)到另一個(gè)頁(yè)面,感興趣的朋友可不要錯(cuò)過(guò)2013-10-10html5+javascript實(shí)現(xiàn)簡(jiǎn)單上傳的注意細(xì)節(jié)
這篇文章主要為大家詳細(xì)介紹了html5+javascript實(shí)現(xiàn)上傳操作的注意細(xì)節(jié),form表單樣式不美觀等細(xì)節(jié)問(wèn)題,感興趣的小伙伴們可以參考一下2016-04-04