JavaScript稀疏數(shù)組與孔hole示例詳解
稀疏數(shù)組是什么
在絕大多數(shù)JavaScript的實現(xiàn)中,數(shù)組是稀疏的,我們可以認為js的數(shù)組都是稀疏的(雖然ES標準并沒有這樣規(guī)定)。
稀疏數(shù)組與密集數(shù)組最大的不同,就是稀疏數(shù)組中可以有“孔”(hole)。孔是邏輯上存在于數(shù)組中,但物理上不存在與內(nèi)存中的那些數(shù)組項。在那些僅有少部分項被使用的數(shù)組中,孔可以大大減少內(nèi)存空間的浪費。比如,我們要表示一個長度為10000的數(shù)組,它的最后一個項是字符串'a'。如果按照密集數(shù)組的做法,我們需要開辟10000個項的空間,有9999個項的空間都被浪費了。而如果按照稀疏數(shù)組的做法,稀疏數(shù)組只需要記錄:“數(shù)組第10000個項的值為'a'”,這節(jié)省了很多內(nèi)存空間。
JavaScript數(shù)組天生就是稀疏數(shù)組
js數(shù)組就是若干個下標(數(shù)字)與值之間的映射。從下標x到值y的映射表示:“數(shù)組第x個項的值為y”。這實際上就是上例中稀疏數(shù)組的記錄方法。
在Chrome控制臺的執(zhí)行結(jié)果
如上圖,如果你調(diào)用new Array(3),你得到的數(shù)組中只有一個屬性length,記錄了它的長度,但是沒有任何下標(數(shù)字)與值之間的映射。這是一個只有3個孔的數(shù)組。
如上圖,如果你繼續(xù)執(zhí)行a[1] = 'aaa',那么實際上是在這個稀疏數(shù)組中增加了一條從1到"aaa"之間的映射。
如上圖,如果你繼續(xù)執(zhí)行a[10000]='bbb',也只不過是又增加了一條從10000到"bbb"之間的映射而已。length自動變?yōu)榱?0001,這符合我們的直覺。不存在映射關(guān)系,但又處在數(shù)組長度范圍內(nèi)的數(shù)組項,就是孔。此時,這個數(shù)組與長度為2的普通數(shù)組['aaa', 'bbb'],占用相同大小的內(nèi)存空間。
JavaScript數(shù)組稀疏特性帶來的“怪異現(xiàn)象”
slice會復制孔
var arr = [ 'a', , 'b' ] // ["a", undefined × 1, "b"] arr.slice(1,2) // [undefined × 1] arr.slice() // ["a", undefined × 1, "b"]
forEach、every會跳過孔(不對孔調(diào)用回調(diào)函數(shù))
var arr = [ 'a', , 'b' ] // ["a", undefined × 1, "b"] arr.forEach(function (x, i) { console.log(i+'.'+x) }) // 0.a // 2.b arr.every(function (x) { return x.length === 1 }) // true
map不對孔調(diào)用回調(diào)函數(shù),但是孔會保留
arr.map(function (x,i) { return i+'.'+x }) // [ '0.a', undefined × 1, '2.b' ]
filter不對孔調(diào)用回調(diào)函數(shù),但是孔會被過濾掉
arr.filter(function (x) { return true }) // [ 'a', 'b' ]
join會將孔轉(zhuǎn)化為一個空字符串進行拼接,與undefined一樣
arr.join('-') // 'a--b' [ 'a', undefined, 'b' ].join('-') // 'a--b'
而其他所有的數(shù)組方法會正常對待孔,就像數(shù)組中真的存在這個“空位”一樣:
var arr2 = arr.slice() arr2.sort() // [ 'a', 'b', undefined × 1 ]
初始化無孔數(shù)組的方法
因為數(shù)組中的孔會造成上述的那些“怪異現(xiàn)象”,所以我們有時希望初始化一個沒有孔的數(shù)組。
比如我們希望初始化[0,1,2]這樣的數(shù)組,但是我們無法通過new Array(3)與map方法得到:
var a1 = new Array(3) // [undefined × 3] a1.map(function (x, i) { return i }) // [undefined × 3] // 因為map會跳過孔,所以實際上回調(diào)函數(shù)沒有被調(diào)用過
a1有孔
正確的方法:
var a2 = Array.apply(null, Array(3)) // [undefined, undefined, undefined] a2.map(function (x, i) { return i }) // [0, 1, 2] // map的回調(diào)函數(shù)執(zhí)行了3次
a2無孔
[undefined × 3]和[undefined, undefined, undefined],chrome控制臺用這兩種表示方式來區(qū)分孔和真正的undefined值!
從上面兩幅圖的對比可以看出,第一種方法沒有構(gòu)造出映射,只創(chuàng)造出了3個孔。而第二種方法創(chuàng)建出了真正的“從下標到值之間的映射”,映射的值為undefined。因此map不會跳過這些數(shù)組項。
Array.apply(null, Array(n))的原理
為什么var a2 = Array.apply(null, Array(3))能創(chuàng)造出無孔的數(shù)組呢?
我們將一個含有3個孔的數(shù)組作為第二個參數(shù)傳遞給apply,apply將利用這個數(shù)組來決定調(diào)用Array()的參數(shù)。
因為apply將數(shù)組中的孔視為undefined,所以Array調(diào)用的參數(shù)實際上為Array(undefined, undefined, undefined)。
又因為通過Array(a,b,c)這種方法調(diào)用Array會返回[a,b,c],所以Array(undefined, undefined, undefined)返回的是[undefined, undefined, undefined]。
參考資料
https://2ality.com/2013/11/initializing-arrays.html
https://2ality.com/2013/07/array-iteration-holes.html
https://2ality.com/2012/06/dense-arrays.html
https://2ality.com/2015/09/holes-arrays-es6.html
以上就是JavaScript稀疏數(shù)組與孔hole示例詳解的詳細內(nèi)容,更多關(guān)于JavaScript稀疏數(shù)組與孔hole的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
YUI Compressor壓縮JavaScript原理及微優(yōu)化
最近寫一個jQuery插件,在最后完成優(yōu)化時,對比發(fā)現(xiàn)壓縮后文件比較大,就思考那些是可以被修改和優(yōu)化的,發(fā)現(xiàn)壓縮原理也有很大的空間可以學習2013-01-01JS動態(tài)增加刪除UL節(jié)點LI及相關(guān)內(nèi)容示例
這篇文章主要介紹了JS如何動態(tài)增加刪除UL節(jié)點LI及相關(guān)內(nèi)容,需要的朋友可以參考下2014-05-05利用javascript實現(xiàn)的三種圖片放大鏡效果實例(附源碼)
這篇文章主要介紹了利用javascript實現(xiàn)的幾種放大鏡效果,很實用一款漂亮的js圖片放大鏡特效,常見于電商網(wǎng)站上產(chǎn)品頁,用來放大展示圖片細節(jié),很有實用性,推薦下載學習研究。文中提供了完整的源碼供大家下載,需要的朋友可以參考借鑒,一起來看看吧。2017-01-01Cropper.js進階之固定寬高圖片裁切實現(xiàn)示例
這篇文章主要為大家介紹了Cropper.js進階之固定寬高圖片裁切實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05AutoJs4.4.1免費版快速接通vscode調(diào)試腳本的操作方法
這篇文章主要介紹了AutoJs4.4.1免費版快速接通vscode進行調(diào)試腳本,首先下載AutoJs并安裝,下載完成后,將2個apk文件拷貝到手機安裝即可,接下來需要安裝插件,本文分步驟給大家介紹的非常詳細,需要的朋友可以參考下2022-10-10