Object.keys?詭異特性示例詳解
先從‘詭異’的問(wèn)題入手
- 例1: 純Number類型的屬性
const obj = { 1: 1, 6: 6, 3: 3, 2: 2 } console.log('keys', Object.keys(obj)) // ['1', '2', '3', '6']
返回的key為什么自動(dòng)按照升序排序了?
- 例2: 純String類型的屬性
const obj2 = { a: 'a', c: 'c', f: 'f', b: 'b', } console.log(Object.keys(obj2)) // ['a', 'c', 'f', 'b']
這里為什么又不自動(dòng)排序了?
看到這里是不是覺(jué)得很懵?話不多說(shuō),我們先查文檔,看看mdn上對(duì)Object.keys
的描述:
Object.keys() 方法會(huì)返回一個(gè)由一個(gè)給定對(duì)象的自身可枚舉屬性組成的數(shù)組,數(shù)組中屬性名的排列順序和正常循環(huán)遍歷該對(duì)象時(shí)返回的順序一致 。
emm,然而它并沒(méi)有說(shuō)到底是按哪種順序返回的。
探索
既然文檔上找不到,那我們就一步一步來(lái)慢慢研究
Object.keys的polyfill的實(shí)現(xiàn)
if (!Object.keys) { Object.keys = (function () { var hasOwnProperty = Object.prototype.hasOwnProperty, hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), dontEnums = [ 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor' ], dontEnumsLength = dontEnums.length; return function (obj) { if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object'); var result = []; for (var prop in obj) { if (hasOwnProperty.call(obj, prop)) result.push(prop); } if (hasDontEnumBug) { for (var i=0; i < dontEnumsLength; i++) { if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); } } return result; } })() };
從Object.keys
的polyfill的實(shí)現(xiàn),我們可以發(fā)現(xiàn)它內(nèi)部其實(shí)是用for...in
來(lái)實(shí)現(xiàn)的。那我們就可以去查找for...in
遍歷時(shí)的順序規(guī)則。然而它也并沒(méi)有介紹遍歷的順序是怎樣的,那么我們就只能去查找ECMAScript
的規(guī)范了。
Object.keys的規(guī)范定義
調(diào)用ToObject(O)
將結(jié)果賦值給變量obj
調(diào)用EnumerableOwnPropertyNames(obj, "key")
將結(jié)果賦值給變量nameList
調(diào)用CreateArrayFromList(nameList)
得到最終的結(jié)果
第一步:將參數(shù)轉(zhuǎn)換成Object(ToObject(O))
因?yàn)镺bject.keys內(nèi)部會(huì)調(diào)用ToObject(O)
方法,所以它不只是可以接受對(duì)象參數(shù),還可以接受其它類型的參數(shù),下面這張表就是ToObject
根據(jù)不同類型的值轉(zhuǎn)成Object的映射:
參數(shù)類型 | 結(jié)果 |
---|---|
Undefined | 拋出TypeError |
Null | 拋出TypeError |
Number | 返回一個(gè)新的 Number 對(duì)象 |
String | 返回一個(gè)新的 String 對(duì)象 |
Boolean | 返回一個(gè)新的 Boolean 對(duì)象 |
Symbol | 返回一個(gè)新的 Symbol 對(duì)象 |
Object | 直接將Object返回 |
我們通常給Object.keys傳的參數(shù)都會(huì)是一個(gè)對(duì)象,但我們也可以來(lái)看看其它類型值的返回值會(huì)是怎樣的?
- Number
console.log(Object.keys(123)) // []
返回的是空數(shù)組,這是因?yàn)閚ew Number(123)并沒(méi)有可提取的屬性
- String
console.log(Object.keys('123')) // [ '0', '1', '2' ]
字符串之所以返回的不是空數(shù)組,是因?yàn)閚ew String('123')有可以提取的屬性
第二步:通過(guò)轉(zhuǎn)換后的對(duì)象獲得屬性列表properties。
(順序取決于這里)
對(duì)象屬性列表是通過(guò) EnumerableOwnPropertyNames
獲取的,其中比較重要的是調(diào)用對(duì)象的內(nèi)部方法OwnPropertyKeys
獲得對(duì)象的ownKeys
(這些內(nèi)容可以在ECMAScript規(guī)范里面找到,就不展開介紹了,我們重點(diǎn)看排序)
The [[OwnPropertyKeys]] internal method of an ordinary object O takes no arguments. It performs the following steps when called:
Return ! OrdinaryOwnPropertyKeys(O).
通過(guò)上面的介紹,我們可以發(fā)現(xiàn)keys的排序取決于 OrdinaryOwnPropertyKeys(O)
翻譯過(guò)來(lái)就是:
- 創(chuàng)建一個(gè)空的列表用于存放 keys
- 將所有合法的數(shù)組索引按升序的順序存入
- 將所有字符串類型索引按屬性創(chuàng)建時(shí)間以升序的順序存入
- 將所有
Symbol
類型索引按屬性創(chuàng)建時(shí)間以升序的順序存入 - 返回 keys
注意:屬性列表properties
為L(zhǎng)ist類型(List類型是ECMAScript規(guī)范類型)
第三步:將List類型的屬性列表properties轉(zhuǎn)換為Array得到最終的結(jié)果。
將List類型的屬性列表轉(zhuǎn)換成Array類型非常簡(jiǎn)單:
- 先聲明一個(gè)變量
array
,值是一個(gè)空數(shù)組 - 循環(huán)屬性列表,將每個(gè)元素添加到
array
中 - 將
array
返回
總結(jié)
Object.keys返回的對(duì)象屬性順序
- 將所有合法的數(shù)組索引按升序排序
- 將所有字符串類型索引按屬性創(chuàng)建時(shí)間以升序排序
- 將所有
Symbol
類型索引按屬性創(chuàng)建時(shí)間以升序排序
合法數(shù)組索引指的是正整數(shù),負(fù)數(shù)或者浮點(diǎn)數(shù)一律當(dāng)做字符串處理。嚴(yán)格來(lái)說(shuō)對(duì)象屬性沒(méi)有數(shù)字類型的,無(wú)論是數(shù)字還是字符串,都會(huì)被當(dāng)做字符串來(lái)處理。
看題
const obj = {} obj[-1] = -1 obj[1] = 1 obj[1.1] = 1.1 obj['2'] = '2' obj['c'] = 'c' obj['b'] = 'b' obj['a'] = 'a' obj[2] = 2 obj[Symbol(1)] = Symbol(1) obj[Symbol('a')] = Symbol('a') obj[Symbol('b')] = Symbol('b') obj['d'] = 'd' console.log(Object.keys(obj))
經(jīng)過(guò)上面對(duì)Object.key
特性的介紹,想必大家都不會(huì)再搞錯(cuò)Object.keys
的輸出順序了吧。
答案:
[ '1', '2', '-1', '1.1', 'c', 'b', 'a', 'd' ]
看到答案很多同學(xué)是不是有很多疑問(wèn)?
如何理解對(duì)象屬性是正整數(shù)還是字符串?
首先我們上面說(shuō)過(guò)合法數(shù)組索引指的是正整數(shù),負(fù)數(shù)或者浮點(diǎn)數(shù)一律當(dāng)做字符串處理。嚴(yán)格來(lái)說(shuō)對(duì)象屬性沒(méi)有數(shù)字類型的,無(wú)論是數(shù)字還是字符串,都會(huì)被當(dāng)做字符串來(lái)處理。
所以上面只有1,'2',2
是合法數(shù)組索引,但我們知道其實(shí)它們都會(huì)被轉(zhuǎn)成字符串,所以后面的2
會(huì)將前面的'2'
覆蓋,然后它們按升序排序。然后負(fù)數(shù)與浮點(diǎn)數(shù)一律當(dāng)做字符串處理按屬性創(chuàng)建時(shí)間以升序排序。這樣就可以得到上面的答案了。
為什么沒(méi)有Symbol類型?
因?yàn)樵?EnumerableOwnPropertyNames
的規(guī)范中規(guī)定了返回值只應(yīng)包含字符串屬性(上面說(shuō)了數(shù)字其實(shí)也是字符串)。
我們也可以在MDN上查看關(guān)于 Object.getOwnPropertyNames()
的描述。
Object.getOwnPropertyNames()
方法返回一個(gè)由指定對(duì)象的所有自身屬性的屬性名(包括不可枚舉屬性但不包括 Symbol 值作為名稱的屬性)組成的數(shù)組。
所以 Symbol 屬性是不會(huì)被返回的,如果要返回 Symbol 屬性可以用 Object.getOwnPropertySymbols()。
以上就是Object.keys 詭異特性示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Object.keys 詭異的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
科訊商業(yè)版中用到的ajax空間與分頁(yè)函數(shù)
科訊商業(yè)版中用到的ajax空間與分頁(yè)函數(shù)...2007-09-0920分鐘成功編寫bootstrap響應(yīng)式頁(yè)面 就這么簡(jiǎn)單
這篇文章主要教大家如何在20分鐘內(nèi)成功編寫bootstrap響應(yīng)式頁(yè)面,其實(shí)很簡(jiǎn)單,培養(yǎng)大家分分鐘開發(fā)出一個(gè)高大上的頁(yè)面能力,感興趣的小伙伴們可以參考一下2016-05-05JavaScript實(shí)現(xiàn)時(shí)鐘功能
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)時(shí)鐘功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06uni-app使用uni.navigateTo傳遞對(duì)象參數(shù)示例代碼
最近在做微信小程序用的是uniapp開發(fā)的,自己記錄一下,也和大家分享一下,這篇文章主要給大家介紹了關(guān)于uni-app使用uni.navigateTo傳遞對(duì)象參數(shù)的相關(guān)資料,需要的朋友可以參考下2023-11-11javascript提取內(nèi)容到作為文章簡(jiǎn)介的代碼
javascript提取文本框內(nèi)容到另一個(gè)文本框,經(jīng)常被用來(lái)做內(nèi)容簡(jiǎn)介的讀取,這樣編輯就不用每次都去處理一篇文章的簡(jiǎn)介了。2011-01-01js函數(shù)與php函數(shù)的區(qū)別實(shí)例淺析
這篇文章主要介紹了js函數(shù)與php函數(shù)的區(qū)別,以實(shí)例形式較為簡(jiǎn)單的分析了js函數(shù)與php函數(shù)語(yǔ)法及應(yīng)用上的不同點(diǎn),具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01