JavaScript 稀疏數組與孔(hole)

在絕大多數JavaScript的實現中,數組是稀疏的,我們可以認為js的數組都是稀疏的(雖然ES標準并沒有這樣規定)。

稀疏數組是什么

稀疏數組與密集數組最大的不同,就是稀疏數組中可以有“孔”(hole)。孔是邏輯上存在于數組中,但物理上不存在與內存中的那些數組項。在那些僅有少部分項被使用的數組中,孔可以大大減少內存空間的浪費。比如,我們要表示一個長度為10000的數組,它的最后一個項是字符串'a'。如果按照密集數組的做法,我們需要開辟10000個項的空間,有9999個項的空間都被浪費了。而如果按照稀疏數組的做法,稀疏數組只需要記錄:“數組第10000個項的值為'a'”,這節省了很多內存空間。

JavaScript數組天生就是稀疏數組

js數組就是若干個下標(數字)與值之間的映射。從下標x到值y的映射表示:“數組第x個項的值為y”。這實際上就是上例中稀疏數組的記錄方法。

在Chrome控制臺的執行結果

如上圖,如果你調用new Array(3),你得到的數組中只有一個屬性length,記錄了它的長度,但是沒有任何下標(數字)與值之間的映射。這是一個只有3個孔的數組。


如上圖,如果你繼續執行a[1] = 'aaa',那么實際上是在這個稀疏數組中增加了一條從1到"aaa"之間的映射


如上圖,如果你繼續執行a[10000]='bbb',也只不過是又增加了一條從10000到"bbb"之間的映射而已。length自動變為了10001,這符合我們的直覺。不存在映射關系,但又處在數組長度范圍內的數組項,就是孔。
此時,這個數組與長度為2的普通數組['aaa', 'bbb'],占用相同大小的內存空間。

JavaScript數組稀疏特性帶來的“怪異現象”

slice會復制孔

var arr = [ 'a', , 'b' ]
// ["a", undefined × 1, "b"]
arr.slice(1,2)
// [undefined × 1]
arr.slice()
// ["a", undefined × 1, "b"]

forEach、every會跳過孔(不對孔調用回調函數)

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不對孔調用回調函數,但是孔會保留

arr.map(function (x,i) { return i+'.'+x })
// [ '0.a', undefined × 1, '2.b' ]

filter不對孔調用回調函數,但是孔會被過濾掉

arr.filter(function (x) { return true })
// [ 'a', 'b' ]

join會將孔轉化為一個空字符串進行拼接,與undefined一樣

arr.join('-')
// 'a--b'
[ 'a', undefined, 'b' ].join('-')
// 'a--b'

而其他所有的數組方法會正常對待孔,就像數組中真的存在這個“空位”一樣:

var arr2 = arr.slice()
arr2.sort()
// [ 'a', 'b', undefined × 1 ]

初始化無孔數組的方法

因為數組中的孔會造成上述的那些“怪異現象”,所以我們有時希望初始化一個沒有孔的數組。
比如我們希望初始化[0,1,2]這樣的數組,但是我們無法通過new Array(3)與map方法得到:

var a1 = new Array(3)
// [undefined × 3]
a1.map(function (x, i) { return i })
// [undefined × 3]
// 因為map會跳過孔,所以實際上回調函數沒有被調用過
a1有孔

正確的方法:

var a2 = Array.apply(null, Array(3))
// [undefined, undefined, undefined]
a2.map(function (x, i) { return i })
// [0, 1, 2]
// map的回調函數執行了3次
a2無孔

[undefined × 3]和[undefined, undefined, undefined],chrome控制臺用這兩種表示方式來區分孔和真正的undefined值!

從上面兩幅圖的對比可以看出,第一種方法沒有構造出映射,只創造出了3個孔。而第二種方法創建出了真正的“從下標到值之間的映射”,映射的值為undefined。因此map不會跳過這些數組項。

Array.apply(null, Array(n))的原理

為什么var a2 = Array.apply(null, Array(3))能創造出無孔的數組呢?
我們將一個含有3個孔的數組作為第二個參數傳遞給apply,apply將利用這個數組來決定調用Array()的參數。
因為apply將數組中的孔視為undefined,所以Array調用的參數實際上為Array(undefined, undefined, undefined)。
又因為通過Array(a,b,c)這種方法調用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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • 數組是值的有序集合。每個值叫做一個元素,而每個元素在數組中有一個位置,以數字表示,稱為索引。 JavaScript...
    劼哥stone閱讀 1,142評論 6 20
  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,264評論 0 4
  • JS基礎講解 JavaScript組成ECMAScript:解釋器、翻譯DOM:Document Object M...
    FConfidence閱讀 579評論 0 1
  • 第三章 類型、值和變量 1、存取字符串、數字或布爾值的屬性時創建的臨時對象稱做包裝對象,它只是偶爾用來區分字符串值...
    坤少卡卡閱讀 650評論 0 1
  • 回到家打開行李的時候,發現了一個小小的青色的梨——“分離分梨”。在周寧縣桃坑村支教的最后一個晚上,我和同行的伙伴們...
    芯遠閱讀 537評論 0 1