在JavaScript中,如果想生成一個大長度的數(shù)組,比如10000,或者100000。大致可以用以下幾種不同的模式
- LOOP方式
通過循環(huán)代碼生成大數(shù)組里面的每一個元素是最簡單的方法。常用的有for,while,do-while。基本的操作大同小異,先生成一個空數(shù)組或者指定長度的數(shù)組。然后采用push往空數(shù)組中添加元素或者通過下標修改指定長度的數(shù)組中的元素。大致代碼如下
// 空數(shù)組模式
var start = new Date().getTime()
var array = []
for (let i=0; i<10000000; i++) {
array.push(i)
}
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 139
// 指定長度數(shù)組模式
var start = new Date().getTime()
var array = Array(10000000)
for (let i=0; i<10000000; i++) {
array[i] = i
}
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 45
- 從一個數(shù)組再生成另一個數(shù)組模式
這種方式本質還是一種loop方式,只是通過對一個已經存在的數(shù)組進行遍歷/迭代,生成一個新的數(shù)組而已。此時使用的方法,有map或者from。代碼如下
// map 方法1
var start = new Date().getTime()
var array = new Array(10000000).fill().map((item,index) => {return index})
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 1829
// map 方法2
var start = new Date().getTime()
var array = new Array(10000000).toString().split(',').map((item,index) => {return index})
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 2071
// map 方法3
var start = new Date().getTime()
var array = Array.apply(null, Array(10000000)).map((item,index) => {return index})
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 32 * 100
// from 方法1
var start = new Date().getTime()
var array = Array.from({length: 10000000}, (v,index) => {return index})
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 1687
// from 方法2
var start = new Date().getTime()
var array = Array.from(Array(10000000), (v,index) => {return index})
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 1436
// from 方法3
var start = new Date().getTime()
var array = Array.from(Array(10000000).keys())
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 1366
var start = new Date().getTime()
var array = [...Array(100000).keys()]
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 32
其中的主要在于如何找到符合長度的已經存在的數(shù)組,一般是新建Array(),需要注意的是。單純的Array(100000)并不能使用map函數(shù),但可以使用from函數(shù)。在MDN的map的解釋如下:
map
方法會給原數(shù)組中的每個元素都按順序調用一次callback
函數(shù)。callback
每次執(zhí)行后的返回值(包括undefined
)組合起來形成一個新數(shù)組。callback
函數(shù)只會在有值的索引上被調用;那些從來沒被賦過值或者使用delete
刪除的索引則不會被調用
在MDN中from的解釋如下:
Array.from()
可以通過以下方式來創(chuàng)建數(shù)組對象:
- 偽數(shù)組對象(擁有一個
length
屬性和若干索引屬性的任意對象)- 可迭代對象(可以獲取對象中的元素,如 Map和 Set 等)
而Array(100000)創(chuàng)建的恰恰是一個稀疏數(shù)組,本質上只是一個proto為Array(0),并擁有一個length屬性為100000的對象。也就是為什么Array(100000)可以在from中使用,但在map中不適用。
- 遞歸方法
遞歸自調用方法,指的是在函數(shù)代碼中,調用自身。遞歸方法需要在代碼中設置跳出條件,一般在執(zhí)行代碼后,進行判斷,再決定是繼續(xù)遞歸調用自身,還是跳出。
var start = new Date().getTime()
var a = []
function arr (length) {
if (a.length < length) {
a.push(a.length)
arr(length)
}
}
arr(10000000)
var end = new Date().getTime()
var cost = end - start
console.log('cost:', cost) // cost: 3 * 1000
性能比較
當需要生成和下標一樣的元素時,循環(huán)使用下標賦值最快。ES6的[...]展開符的時間也相差無幾。當需要生成同一元素時,fill和下標賦值最快。
1:push vs [i]
在使用push的時候,每次JS需要先找到當前索引的最大值,再在此基礎上生成新的索引值,然后對新的索引值進行賦值。而[i]則是直接對相應的索引賦值。
2:map和from
兩者的性能相差不大。都要比[i]下標方法,因為在每一個元素的生成過程中,都存在,先根據(jù)索引取元素,再根據(jù)callback進行執(zhí)行,最后將結果賦值到對應新數(shù)組的索引中去。
3:[...Array(10000).keys()] 和 Array(10000).fill(1)
這兩種方法一個用于生成下標元素,一個用于生成同一元素,性能和[i]差不多。keys()返回一個迭代器,只需要每次執(zhí)行迭代,然后將數(shù)據(jù)賦值到新的元素上。執(zhí)行過程和[i]類似,fill()的執(zhí)行同理。結論
對于數(shù)組來講,它本身是一個Object。其中的索引等同于Object的key,且以字符的形式表示。所以a[100] == a['100']。Array的length只一個表征當前Array中最大可用索引(數(shù)字)的子項。代碼如下
var a = []
a['t'] = 1
console.log(a.length) // 0
a[10] = 2
console.log(a.length) // 11
a['20'] = 3
console.log(a.length) // 21
因此,對與JS的數(shù)組來講,當操作大批量的元素的時候,直接根據(jù)下標操作,反而要比經過各種常見的迭代方法要快。