起因
今天跟一位程序員朋友日常聊天,聊到了一道面試題:
請用JS實現(xiàn)一個函數(shù)InsertItemToArray先鱼,
函數(shù)接受三個參數(shù):要插入元素的數(shù)組奸鬓、要插入的新元素、新元素所在的位置(arr, item, index)宏多,
往原數(shù)組arr的index位置插入一個新的元素item澡罚,index之后的元素索引依次后移,并返回插入新元素后的新數(shù)組更胖。要求:不允許用splice,slice。
這道題讀完題目后巢音,慣性思路就是寫一個for循環(huán),比如:
const InsetItemToArray = (arr, item, index) => {
const resultArr = [];
for(let i = 0; i < arr.length + 1; i++){
if (i < index){
resultArr.push(arr[i]);
} else if (i > index) {
resultArr.push(arr[i - 1]);
} else {
resultArr[i] = item;
}
}
return resultArr;
}
InsetItemToArray([1,2,3], 'a', 1);
// [1, 'a', 2, 3]
或者換個思路:
const InsetItemToArray = (arr, item, index) => {
const resultArr = [...arr];
for(let i = arr.length; i > index; i--){
resultArr[i] = arr[i - 1];
}
resultArr[index] = item;
return resultArr;
}
InsetItemToArray([1,2,3], 'b', 1);
// [1, 'b', 2, 3]
以上都是比較常用且不用花太多思考時間的寫法。
轉(zhuǎn)折
這時候突然覺得账锹,這道題其實可以用解構(gòu)賦值的模式匹配來實現(xiàn)坷襟。
我們知道婴程,es6的解構(gòu)賦值是可以這樣寫的:
const arr = [1, 2, 3, 4, 5];
const [ , ,...resultArr] = arr;
// resultArr => [3, 4, 5]
那我們可以把返回結(jié)果看成是兩個部分:
- 第一部分是
headArr
档叔,包含原數(shù)組arr
中索引值小于index
的元素和即將插入的新元素item
- 第二部分是
footArr
蒸绩,包含原數(shù)組arr
中索引值大于或等于index
的元素
最后將兩個部分拼在一起,即是要返回的新數(shù)組了传蹈。
我們先來看怎么得到footArr
步藕。
// 錯誤示例
const GetFootArr = (arr, item, index) => {
const placeArr = new Array(index);
const [...placeArr, ...footArr] = arr;
return footArr;
}
// 控制臺拋出一個錯誤:Uncaught SyntaxError: Rest element must be last element
奇怪了,讓我們對比一下結(jié)果:
// 正常解構(gòu)
const arr = [1, 2, 3, 4, 5];
const [ , ,...resultArr] = arr;
// resultArr => [3, 4, 5]
// 報錯
const arr = [1, 2, 3, 4, 5];
const placeArr = new Array(2);
const [...placeArr, ...resultArr] = arr;
// Uncaught SyntaxError: Rest element must be last element
這時候打印placeArr
console.log(placeArr);
// [empty × 2]
打印[...palceArr]
console.log([...placeArr]);
// [undefined, undefined]
我們發(fā)現(xiàn)原本用來占位的empty
都變成了undefined
沾歪。
結(jié)論
數(shù)組的空位指灾搏,數(shù)組的某一個位置沒有任何值立润。比如,Array
構(gòu)造函數(shù)返回的數(shù)組都是空位蕾域。
就像剛才打印placeArr
得到的結(jié)果都是empty
一樣。
但是要注意:
空位并不是undefined
搁骑,一個位置的值等于undefined
又固,依然是有值的⊙笾唬空位是沒有任何值妒茬,in
運算符可以說明這一點团赁。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
上面代碼說明,第一個數(shù)組的 0 號位置是有值的析蝴,第二個數(shù)組的 0 號位置沒有值吞滞。
擴展運算符(...)是會將空位轉(zhuǎn)為undefined。
[...['a',,'b']]
// [ "a", undefined, "b" ]
除了擴展運算符,entries()
谭确、keys()
问顷、values()
出皇、find()
和findIndex()
都會將空位處理成undefined
纱注。
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
而數(shù)組解構(gòu)賦值的模式匹配,并不能匹配undefined
作為變量名。所以placeArr
就不能被正常的賦值。
關(guān)于數(shù)組空位的解釋扎谎,可以參考阮一峰老師的ECMAScript 6 入門 - 數(shù)組的擴展。
這里不做多的引申觅够。
PS
既然解構(gòu)賦值的思路行不通廷粒,但是我們?nèi)耘f可以按照headArr
和footArr
的思路來解這道題嗤放,只需換一種寫法岳服。
比如:
const InsetItemToArray = (arr, item, index) => {
const headArr = arr.filter((_, i) => i < index);
const footArr = arr.filter((_, i) => i >= index);
return [ ...headArr, item, ...footArr];
}
InsetItemToArray([1,2,3], 'c', 2);
// [1, 2, 'c', 3]
這樣一來我們就得到了正確的結(jié)果了文兑。
完籍铁。