稀疏陣列和密集陣列
稀疏陣列 Sparse arrays
一般來說,JavaScript 中的陣列都是稀疏陣列-它們可以擁有空槽,所謂空槽,指的就是陣列的某個位置沒有任何值,既不是 undefined
,也不是 null
,因為陣列只是索引到值的簡單對映。
let a = new Array(3);
console.log(a); // (3) [空 × 3]
console.log(a[0]); // undefined
a.forEach(function (x, i) { console.log(i, x) }); // 沒有列印任何東西
a.map(function (x, i) { return i }) // (3) [空 × 3]
密集陣列 Dense arrays
let a = Array.apply(null, Array(3)); // 相當於 Array(undefined, undefined, undefined)
console.log(a); // [ undefined, undefined, undefined ]
console.log(a[0]); // undefined
a.forEach(function (x, i) { console.log(i+". "+x) });
// 0 undefined
// 1 undefined
// 2 undefined
a.map(function (x, i) { return i }) // [ 0, 1, 2 ]
位移操作符
按位移動操作符有兩個運算元:第一個是要被移動的數字,而第二個是要移動的長度。
移動的方向根據操作符的不同而不同。
按位移動會先將運算元轉換為大端位元組序順序(big-endian order)的32位整數,並返回與左運算元相同型別的結果。右運算元應小於 32位,否則只有最低 5 個位元組會被使用。
Big-Endian: 高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端,又稱為"高位編址"。
Big-Endian是最直觀的位元組序:
- 把記憶體地址從左到右按照由低到高的順序寫出;
- 把值按照通常的高位到低位的順序寫出;
- 兩者對照,一個位元組一個位元組的填充進去。
<<
左移
該操作符會將第一個運算元向左移動指定的位數。向左被移出的位被丟棄,右側用 0 補充。
9 (base 10) -> 00000000000000000000000000001001 (base 2)
console.log(9 << 2); // 36
// 00000000000000000000000000001001 -> 00000000000000000000000000100100 = 36 (base 10)
>>
有符號右移
該操作符會將第一個運算元向右移動指定的位數。向右被移出的位被丟棄,拷貝最左側的位以填充左側。由於新的最左側的位總是和以前相同,符號位沒有被改變。所以被稱作符號傳播。
console.log(9 >> 2); // 2
// 00000000000000000000000000001001 -> 00000000000000000000000000000010 = 2 (base 10)
console.log(-9 >> 2 ); // -3 因為符號被保留了
// 11111111111111111111111111110111 -> 11111111111111111111111111111101 = -3 (base 10)
>>>
無符號右移
該操作符會將第一個運算元向右移動指定的位數。向右被移出的位被丟棄,左側用0填充。因為符號位變成了 0,所以結果總是非負的。(譯註:即便右移 0 個位元,結果也是非負的。)
對於非負數,有符號右移和無符號右移總是返回相同的結果。例如 9 >>> 2 和 9 >> 2 一樣返回 2:
console.log(9 >>> 2); // 2
// 00000000000000000000000000001001 -> 00000000000000000000000000000010 = 2 (base 10)
但是對於負數卻不盡相同。 -9 >>> 2 產生 1073741821 這和 -9 >> 2 不同:
console.log(-9 >>> 2); // 1073741821
// 11111111111111111111111111110111 -> 00111111111111111111111111111101 = 1073741821 (base 10)
lodash.slice
console.log([1, 2, 3, 4].slice(-1))
/**
* 裁剪陣列,從 start 位置開始到 end 結束,但不包括 end 本身的位置。
* 代替 Array.prototype.slice,確保密集陣列被返回。
*
* @param {Array} array 要裁剪的陣列
* @param {number} [start=0] 開始的位置 | 負指數將被視為距結束的偏移量。
* @param {number} [end=array.length] 結束的位置 | 負指數將被視為距結束的偏移量。
* @returns {Array} 返回裁切後的陣列
* @example
*
* var array = [1, 2, 3, 4]
* _.slice(array, 2)
* // => [3, 4]
*/
function slice(array, start, end) {
// 若 array 為 null 返回 []
let length = array == null ? 0 : array.length
if (!length) {
return []
}
// 若 start 為 null 取第 0 位
start = start == null ? 0 : start
// 若 end 為 undefined(沒傳該值),end 取陣列 length
end = end === undefined ? length : end
// start 為負值,若大於陣列長度,start 取第零位,否則從最後以為往前減
if (start < 0) {
start = -start > length ? 0 : (length + start)
}
// 若結束位置大於陣列 length 取 length
end = end > length ? length : end
if (end < 0) {
end += length
}
// x >>> 0 保證 x 為數字型別且為正整數,在無意義的情況下預設值為0。
length = start > end ? 0 : ((end - start) >>> 0)
// start = start >>> 0
start >>>= 0
// 構建新陣列並返回
let index = -1
const result = new Array(length)
while (++index < length) {
result[index] = array[index + start]
}
return result
}
export default slice