標簽(空格分隔): call apply
筆記版
編寫代碼中通常會有快速初始化數(shù)組的需求毛仪,例如我們需要一個類似matlab里的zeros
函數(shù)宦棺,假如這里我們需要生成一個0-23的數(shù)組用于表示一天24小時。
最基本的做法如下:
function(){
let hours = [];
for(let k = 0; k < 24; k++ )hours.push(k);
return hours;
}
下面我們來思考如何用更優(yōu)雅的方式實現(xiàn)。
考慮使用new Array(24)
+ map
的方法來實現(xiàn)踪宠。
代碼如下:
Array(24).map((_, h) => h);
注意,這里map
的第二個參數(shù)是索引妈嘹,平時用的少柳琢,這里把索引作為數(shù)值。
結果與預期并不符合润脸,為啥呢柬脸?
簡單搜索了一下,發(fā)現(xiàn)時因為js里的稀疏數(shù)組的邏輯導致的毙驯。
我們先看一下下面的代碼:
let a = [];
a[1000] = 2;
console.log(a.length);
// 1000
a.forEach(x => console.log("hello"));
// only one "hello"
js的處理邏輯是倒堕,對于沒有主動賦值的位置進行“空置”處理,對于這些“空置”未知爆价,迭代器是不會理會的垦巴,這么做最主要的目的就是避免不合理的賦值操作導致的bug。
假設沒有這種邏輯铭段,我們寫下了new Array(Date.now())
骤宣,這將導致系統(tǒng)新建一個非常大的數(shù)組,而實際上啥也沒存序愚。
我們可以吧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)
這種寫法來初始化數(shù)組活逆,但是這么寫就沒法實現(xiàn)我們編寫初始化數(shù)組的目的了。
這個時候我們想到了apply
拗胜,這個函數(shù)的第二個參數(shù)正好就是一個數(shù)組蔗候,于是我們寫下了下面的代碼。
// 借用apply
Array.apply(null, Array(24)).map((_, h) => h);
// [0, 1, ..., 24]
得到了我們希望的結果埂软。這就說明锈遥,Array(24)
在apply
中作為參數(shù)的時候是被當做24個值對待的纫事,因為這一點就保證了最后得到的數(shù)組長度是24。
既然如此所灸,我們當然同樣可以使用apply
的姊妹函數(shù)call
丽惶。
// 借用call
Array.call(null, ...Array(24)).map((_, h) => h);
// [0, 1... 23]
這也更確認了一件事,Array(len)
解構會得到len
個參數(shù)而非一個參數(shù)爬立,當然call
的使用必須在支持解構操作符的環(huán)境中钾唬。
在熟悉了call
和apply
的原理后,我們可以進一步寫出下面的代碼:
// Array本身
Array(...Array(24)).map((_, h) => h);
// [0, 1, ..., 24]
這種形式已經(jīng)足夠優(yōu)雅了侠驯。
另外抡秆,在ES6
中,Array
提供了新方法fill
吟策,借用該方法填充那些“空置”位儒士,進而保證后續(xù)的操作能順利進行。
具體代碼如下:
// 推薦
Array(24).fill(null).map((_, h) => h);
現(xiàn)在也比較推薦最后一種寫法檩坚,這種方法也最為直觀着撩。
不過需要注意fill
方法的使用,應該盡量避免盲目地填充匾委,因為這樣會上面提到了js設計“空置”邏輯為了避免的bug睹酌。
有興趣的可以嘗試一下下面的代碼:
// no-fill
console.time("no-fill");
let t = Array(5e7);
console.timeEnd("no-fill");
// fill
console.time("fill");
let q = Array(5e7).fill(null);
console.timeEnd("fill");
// => no-fill: 0.240ms
// => fill: 3247.921ms
在瀏覽器中嘗試瀏覽器基本會不響應,沒有設置更大的數(shù)值為了避免得不到結果剩檀。假設一個int占用4個字節(jié)的內存憋沿,這個fill會占用200M的內存,且需要循環(huán)5千萬次沪猴,這必然會占用大量的CPU和內存辐啄,在單線程的js中是絕對不允許的。