JavaScript 稀疏數(shù)組與孔(hole)

在絕大多數(shù)JavaScript的實(shí)現(xiàn)中节预,數(shù)組是稀疏的,我們可以認(rèn)為js的數(shù)組都是稀疏的(雖然ES標(biāo)準(zhǔn)并沒有這樣規(guī)定)属韧。

稀疏數(shù)組是什么

稀疏數(shù)組與密集數(shù)組最大的不同,就是稀疏數(shù)組中可以有“孔”(hole)蛤吓。孔是邏輯上存在于數(shù)組中宵喂,但物理上不存在與內(nèi)存中的那些數(shù)組項(xiàng)。在那些僅有少部分項(xiàng)被使用的數(shù)組中会傲,孔可以大大減少內(nèi)存空間的浪費(fèi)锅棕。比如,我們要表示一個長度為10000的數(shù)組淌山,它的最后一個項(xiàng)是字符串'a'裸燎。如果按照密集數(shù)組的做法,我們需要開辟10000個項(xiàng)的空間泼疑,有9999個項(xiàng)的空間都被浪費(fèi)了德绿。而如果按照稀疏數(shù)組的做法,稀疏數(shù)組只需要記錄:“數(shù)組第10000個項(xiàng)的值為'a'”退渗,這節(jié)省了很多內(nèi)存空間移稳。

JavaScript數(shù)組天生就是稀疏數(shù)組

js數(shù)組就是若干個下標(biāo)(數(shù)字)與值之間的映射。從下標(biāo)x到值y的映射表示:“數(shù)組第x個項(xiàng)的值為y”会油。這實(shí)際上就是上例中稀疏數(shù)組的記錄方法个粱。

在Chrome控制臺的執(zhí)行結(jié)果

如上圖囊蓝,如果你調(diào)用new Array(3)酣栈,你得到的數(shù)組中只有一個屬性length慢睡,記錄了它的長度乾戏,但是沒有任何下標(biāo)(數(shù)字)與值之間的映射兔乞。這是一個只有3個孔的數(shù)組创坞。


如上圖异吻,如果你繼續(xù)執(zhí)行a[1] = 'aaa'怨规,那么實(shí)際上是在這個稀疏數(shù)組中增加了一條從1到"aaa"之間的映射絮吵。


如上圖弧烤,如果你繼續(xù)執(zhí)行a[10000]='bbb',也只不過是又增加了一條從10000到"bbb"之間的映射而已蹬敲。length自動變?yōu)榱?0001暇昂,這符合我們的直覺。不存在映射關(guān)系伴嗡,但又處在數(shù)組長度范圍內(nèi)的數(shù)組項(xiàng)急波,就是孔。
此時瘪校,這個數(shù)組與長度為2的普通數(shù)組['aaa', 'bbb']澄暮,占用相同大小的內(nèi)存空間名段。

JavaScript數(shù)組稀疏特性帶來的“怪異現(xiàn)象”

slice會復(fù)制孔

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)化為一個空字符串進(jì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ù)組的方法

因?yàn)閿?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]
// 因?yàn)閙ap會跳過孔静稻,所以實(shí)際上回調(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)建出了真正的“從下標(biāo)到值之間的映射”押搪,映射的值為undefined。因此map不會跳過這些數(shù)組項(xiàng)梗掰。

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ù)及穗。
因?yàn)?strong>apply將數(shù)組中的孔視為undefined摧茴,所以Array調(diào)用的參數(shù)實(shí)際上為Array(undefined, undefined, undefined)。
又因?yàn)橥ㄟ^Array(a,b,c)這種方法調(diào)用Array會返回[a,b,c]埂陆,所以Array(undefined, undefined, undefined)返回的是[undefined, undefined, undefined]苛白。


參考資料

http://2ality.com/2012/06/dense-arrays.html
http://2ality.com/2013/07/array-iteration-holes.html
http://2ality.com/2013/11/initializing-arrays.html
http://2ality.com/2015/09/holes-arrays-es6.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市焚虱,隨后出現(xiàn)的幾起案子购裙,更是在濱河造成了極大的恐慌,老刑警劉巖鹃栽,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躏率,死亡現(xiàn)場離奇詭異,居然都是意外死亡民鼓,警方通過查閱死者的電腦和手機(jī)薇芝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丰嘉,“玉大人夯到,你說我怎么就攤上這事∫鳎” “怎么了耍贾?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵阅爽,是天一觀的道長。 經(jīng)常有香客問我荐开,道長付翁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任晃听,我火速辦了婚禮胆敞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杂伟。我一直安慰自己,他們只是感情好仍翰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布赫粥。 她就那樣靜靜地躺著,像睡著了一般予借。 火紅的嫁衣襯著肌膚如雪越平。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天灵迫,我揣著相機(jī)與錄音秦叛,去河邊找鬼。 笑死瀑粥,一個胖子當(dāng)著我的面吹牛挣跋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狞换,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼避咆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了修噪?” 一聲冷哼從身側(cè)響起查库,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黄琼,沒想到半個月后樊销,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脏款,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年围苫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弛矛。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡够吩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丈氓,到底是詐尸還是另有隱情周循,我是刑警寧澤强法,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站湾笛,受9級特大地震影響饮怯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嚎研,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一蓖墅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧临扮,春花似錦论矾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蚜退,卻和暖如春闰靴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钻注。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工蚂且, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幅恋。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓杏死,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捆交。 傳聞我的和親對象是個殘疾皇子识埋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 數(shù)組是值的有序集合。每個值叫做一個元素零渐,而每個元素在數(shù)組中有一個位置窒舟,以數(shù)字表示,稱為索引诵盼。 JavaScript...
    劼哥stone閱讀 1,130評論 6 20
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,233評論 0 4
  • JS基礎(chǔ)講解 JavaScript組成ECMAScript:解釋器惠豺、翻譯DOM:Document Object M...
    FConfidence閱讀 572評論 0 1
  • 第三章 類型、值和變量 1风宁、存取字符串洁墙、數(shù)字或布爾值的屬性時創(chuàng)建的臨時對象稱做包裝對象,它只是偶爾用來區(qū)分字符串值...
    坤少卡卡閱讀 637評論 0 1
  • 回到家打開行李的時候,發(fā)現(xiàn)了一個小小的青色的梨——“分離分梨”饮寞。在周寧縣桃坑村支教的最后一個晚上孝扛,我和同行的伙伴們...
    芯遠(yuǎn)閱讀 525評論 0 1