標籤(空格分隔): call apply
#編寫程式碼中通常會有快速初始化陣列的需求,例如我們需要一個類似matlab裡的zeros
函數,假如這裡我們需要產生一個0-23的陣列用來表示一天24小時。
最基本的做法如下:
function(){ let hours = []; for(let k = 0; k < 24; k++ ) hours.push(k); return hours; }
下面我們來思考如何用更優雅的方式實現。
考慮使用new Array(24)
+ map
的方法來實作。
程式碼如下:
Array(24).map((_, h) => h);
注意,這裡map
的第二個參數是索引,平常用的少,這裡把索引當作數值。
結果與預期並不符合,為啥呢?
簡單搜尋了一下,發現時因為js裡的稀疏數組的邏輯導致的。
我們先來看看下面的程式碼:
let a = []; a[1000] = 2; console.log(a.length);// 1000a.forEach(x => console.log("hello"));// only one "hello"
js的處理邏輯是,對於沒有主動賦值的位置進行「空置」處理,對於這些「空置」未知,迭代器是不會理會的,這麼做最主要的目的就是避免不合理的賦值操作所導致的bug。
假設沒有這種邏輯,我們寫下了new Array(Date.now())
,這將導致系統新建一個非常大的數組,而實際上啥也沒存。
我們可以吧new Array(len)
幹的事情簡單理解為下面的過程:
function(len){ let r = []; r.length = len; return r;
這就是為什麼對new Array(len)
調用map
或forEach
的時候跟預期不一致了。
如何解決這個問題呢,除了使用new Array(len)
的形式,我們還可以使用new Array(1,2,3)
這種寫法來初始化數組,但是這麼寫就沒辦法實現我們寫初始化數組的目的了。
這時候我們想到了apply
,這個函數的第二個參數剛好就是一個數組,於是我們寫下了下面的程式碼。
// 借用apply Array.apply(null, Array(24)).map((_, h) => h); // [0, 1, ..., 24]
得到了我們希望的結果。這就說明,Array(24)
在apply
中當作參數的時候是被當做24個值對待的,因為這一點就保證了最後得到的陣列長度是24。
既然如此,我們當然同樣可以使用apply
的姊妹函數call
。
// 借用call Array.call(null, ...Array(24)).map((_, h) => h); // [0, 1... 23]
這也更確認了一件事,Array(len)
解構會得到len
個參數而非一個參數,當然call
的使用必須在支援解構操作符的環境中。
在熟悉了call
和apply
的原理後,我們可以進一步寫出下面的程式碼:
// Array本身 Array(...Array(24)).map((_, h) => h); // [0, 1, ..., 24]
這種形式已經足夠優雅了。
另外,在ES6
中,Array
提供了新方法fill
,借用該方法填充那些「空置」位,進而保證後續的操作能順利進行。
具體程式碼如下:
// 推荐 Array(24).fill(null).map((_, h) => h);
現在也比較推薦最後一種寫法,這種方法也最為直覺。
不過要注意fill
方法的使用,應該盡量避免盲目地填充,因為這樣會上面提到了js設計「空置」邏輯為了避免的bug。
有興趣的可以嘗試下面的程式碼:
// no-fillconsole.time("no-fill");let t = Array(5e7);console.timeEnd("no-fill");// fillconsole.time("fill");let q = Array(5e7).fill(null);console.timeEnd("fill");// => no-fill: 0.240ms// => fill: 3247.921ms
在瀏覽器中嘗試瀏覽器基本上會不回應,沒有設定更大的數值為了避免得不到結果。假設一個int佔用4個位元組的內存,這個fill會佔用200M的內存,而且需要循環5千萬次,這必然會佔用大量的CPU和內存,在單線程的js中是絕對不允許的。
#以上是如何用js快速初始化數組的詳細內容。更多資訊請關注PHP中文網其他相關文章!