JavaScript中普通屬性和排序屬性詳解
普通屬性
在JavaScript
中定義對象時,普通的以字符串為鍵
定義的屬性為普通屬性
,特點是在遍歷對象屬性時根據創(chuàng)建時的順序排序
const obj = { 'name': 'Bruse', 'age': 16, } for (const key in obj) { console.log(key) }
輸出順序為
name
age
排序屬性
排序屬性則是以數字為鍵
定義的屬性,特點是按照索引值的大小進行升序排序
,優(yōu)先級優(yōu)于普通屬性。
const obj = { 'name': 'Bruse', // 普通屬性 'age': 16, // 普通屬性 2: 'obj2' // 排序屬性 } obj[1] = 'obj1' // 排序屬性 obj[99] = 'obj99' // 排序屬性 obj[10] = 'obj10' // 排序屬性 for (const key in obj) { console.log(key) }
輸出
1
2
10
99
name
age
可以看到在使用for ... in
遍歷obj
的屬性時,排序屬性
遍歷順序優(yōu)先于普通屬性
,同時排序屬性
也是按照鍵值進行升序排序
的,鍵值越小,遍歷順序越往前
。
字符串數字作為鍵
在obj
中新建一個鍵為'3'
,值為112
的屬性,遍歷obj
屬性名并輸出
const obj = { 'name': 'Bruse', // 普通屬性 'age': 16, // 普通屬性 2: 'obj2', // 排序屬性 '3': 112 // 轉換,也是排序屬性 } obj[1] = 'obj1' // 排序屬性 obj[99] = 'obj99' // 排序屬性 obj[10] = 'obj10' // 排序屬性 for (const key in obj) { console.log(key) }
輸出
1
2
3
10
99
name
age
可以看到即便在定義屬性鍵時,是字符串類型的數字3,也會做一下轉換,將其轉換為排序屬性
排序屬性 VS 普通屬性
存儲方式
首先排序屬性
和普通屬性
在對象中的存儲方式不一樣
可以簡單地理解為obj
對象中,分別有著elements
和properties
兩個內置的屬性,elements
可以理解為是一個數組,而數字鍵則是屬性在該數組中的下標,也就是內存偏移量。通過下標訪問數組中的某個元素是很快的。當我們要訪問obj.1
時,會先從obj
對象中內置的elements
屬性中通過1
這個下標進行查詢,最終找到obj[1]
的值為obj1
。
而properties
可以理解為是一個Map
,而字符串鍵則是該Map
中的key
,value
則是對應的屬性值。當訪問obj.name
時,會先從elements
中進行查找,結果是找不到,然后再從properties
中進行查找,但從properties
中查找則沒有elements
中查找快,因為elements
可以通過計算偏移量來進行訪問,但是properties
要hash計算訪問。
對象內屬性
其實上圖還并不是全貌,因為即便有著elements
和properties
分別存放排序屬性和普通屬性,但是無論訪問排序屬性和普通屬性[排序屬性訪問速度優(yōu)于普通屬性]
,都是要先訪問obj
這個對象的elements
或properties
內置屬性,然后再通過屬性鍵訪問到具體的屬性值,其實就相當于obj.name
= obj.properties.name
,還存在優(yōu)化空間,這個時候對象內屬性
就出場了。
對象內屬性
其實就是被保存到對象自身的常規(guī)屬性
,也就是真正意義上的讓obj.name
不再等于obj.properties.name
,而是真正的所見即所得obj.name
代碼測試
為了方便理解,把之前的代碼稍作修改
class People { constructor() { this.name = 'Bruse' this.age = 16 this[2] = 'obj2' } } const obj = new People() obj[1] = 'obj1' // 排序屬性 obj[99] = 'obj99' // 排序屬性 obj[10] = 'obj10' // 排序屬性 debugger // 避免執(zhí)行過快,導致還來不及生成內存快照,obj對象就已被回收 for (const key in obj) { console.log(key) }
F12
打開瀏覽器開發(fā)者工具,當debugger
阻塞住代碼往下執(zhí)行時,點擊Memory
,生成內存快照
可以看到obj
這個對象中存在內置屬性elements
和一些內屬性name
、[10]
、[99]
、[1]
、[2]
,暫時并沒有內置屬性properties
首先因為obj
屬性并不多,此時對象內屬性的數量還比較少
,所以此時并不需要內置屬性properties
來存放普通屬性,而是將name
等普通屬性當做是對象的內置屬性存放即可,訪問速度比訪問elements
和properties
更快。
Tips:不要看到elements中10、99排在1、2前面,就以為elements不是按照數字升序排列的,因為輸出之后你會發(fā)現(xiàn)其實還是1、2、10、99這樣的順序,至于為什么在調試工具里看起來順序有點不太一致,我也不知道...
添加更多的排序屬性
接下來給obj
塞入更多的排序屬性
class People { constructor() { this.name = 'Bruse' this.age = 16 } } const obj = new People() for (let i = 0; i < 20; i++) { obj[i] = `obj${i}` } debugger for (const key in obj) { console.log(key) }
再看看看obj
的變化,首先elements
屬性中的元素變多了
同時也并沒有出現(xiàn)內置屬性properties
因為只是增加了更多的排序屬性
,并沒有突破內置屬性(內置常規(guī)屬性)的數量限制
添加更多的普通屬性
接下來再塞入更多的普通屬性
class People { constructor() { this.name = 'Bruse' this.age = 16 for (let i = 0; i < 20; i++) { this[`obj${i}`] = i } } } const obj = new People() debugger for (const key in obj) { console.log(key) }
可以看到在這個時候obj
的內置屬性properties
終于出現(xiàn)了
只不過貌似并不像elements
那樣方便預覽其中的屬性...后來經過一番查找和嘗試,終于是找到了解決辦法... 也很簡單,就是生成內存快照時多做一步操作,勾上“在快照中添加數字值”
再次內存分析,好吧...因為生成20個常規(guī)變量的緣故,貌似也還沒達到內置屬性的限制...
將數量調整到50個,可以看到elements
和properties
都有了相應存放的變量
for (let i = 0; i < 50; i++) { this[i] = `obj${i}` this[`obj${i}`] = i }
困惑
單純在Chrome上進行內存分析的話,貌似即便超出了內屬性數量限制,那多出來的一部分普通屬性,也非常直白地展示在properties
之外,這個暫時不太清楚...
總結
把上述排序屬性``普通屬性``內屬性
結合到一塊來看,那么JavaScript
中的對象屬性存放結構應該如下圖所示
首先是為了提高訪問速度,對象obj
本身就會有一定空間存放內屬性
,在訪問內屬性
時,可以直接跳過訪問elements
或properties
這一步,直接訪問到該屬性的值。
但是內屬性
是有一定數量限制的,所以當超出了限制后,剩下的普通屬性
會被存放到properties
中,而properties
有點像Map
,在進行屬性訪問的時候,需要計算出鍵的hash值,然后才能訪問到具體的屬性值。
而elements
則是存放排序屬性
用的,有點像Array
,數字鍵即數組中的下標,所有元素按數字升序進行排序,訪問屬性值時則是通過下標進行訪問,訪問速度會比properties
要快一些。
properties
和elements
兩種存儲方式之間的VS,本質上就像是數據結構中的Map
和Array
之間的VS。
以上就是JavaScript中普通屬性和排序屬性詳解的詳細內容,更多關于JavaScript屬性的資料請關注腳本之家其它相關文章!
相關文章
理解javascript函數式編程中的閉包(closure)
這篇文章主要幫助大家理解javascript函數式編程中的閉包(closure)概念,通俗地講, JavaScript 中每個的函數都是一個閉包,感興趣的小伙伴們可以參考一下2016-03-03JavaScript中 創(chuàng)建動態(tài) QML 對象的方法
這篇文章主要介紹了 JavaScript中 創(chuàng)建動態(tài) QML 對象,下面文章主要分析了兩中方法,分別是從 QML 字符串創(chuàng)建對象和動態(tài)創(chuàng)建組件以及刪除對象等方法,具有有一定的參考價值,需要的小伙伴可以參考一下2021-12-12使用 JavaScript 創(chuàng)建并下載文件(模擬點擊)
本文將介紹如何使用 JavaScript 創(chuàng)建文件,并自動/手動將文件下載,這在導出原始數據時會比較方便2019-10-10Javascript基于對象三大特性(封裝性、繼承性、多態(tài)性)
這篇文章主要介紹了Javascript基于對象三大特性,包括封裝性、繼承性、多態(tài)性,感興趣的小伙伴們可以參考一下2016-01-01silverlight線程與基于事件驅動javascript引擎(實現(xiàn)軌跡回放功能)
前一段時間一直在重構工作站軌跡回放功能,一開始我覺得很簡單,但是后面引發(fā)了一系列奇怪的問題,讓我很疼,所以不得不寫個總結加深記憶,2011-08-08