JS 数组常见需求

(一)数组常见需求

检验数组

不能使用 typeof 方法检验一个变量是否为数组,因为它的检测结果只有 undefinedobjectfunctionbooleanstringnumberbigintsymbol

1
2
var arr = ['a', 'b', 'c'];
console.log(typeof(arr)); //"object"

推荐使用 Array 自带的 isArray() 方法进行检验。

或者使用 Object.prototype.toString.call(arr).slice(8, -1)==="Array" 进行判断。

1
2
console.log(Array.isArray(arr)); // true
console.log(Object.prototype.toString.call(arr)); // "[object Array]"

不能直接使用 arr.toString() 来确定类型的原因在于,Object 的 toString 方法被其实例 Array 进行了重写。当数组对象调用自身的 toString 方法时,会用逗号拼接数组中的所有值,并返回其字符串形式。

1
console.log(arr.toString()); // "a,b,c"

数组去重

1
2
3
4
5
6
7
8
9
10
11
12
13
test.push('a'); // ["a", "b", "c", "a"]

// ES5
console.log(new Set(test)); // {"a", "b", "c"}
var test1 = Array.from(new Set(test));
console.log(test1); // ["a", "b", "c"]

var test2 = test.filter((item, index) => test.indexOf(item) === index);
console.log(test2); // ["a", "b", "c"]

// ES6
var test3 = [...new Set(test)]
console.log(test3) // ["a", "b", "c"]

数组扁平化

数组扁平化就是将 [1, [2, [3]]] 这种多层的数组拍平成一层 [1, 2, 3]。使用 Array.prototype.flat 可以直接将多层数组拍平成一层:

1
[1, [2, [3]]].flat(2)  // [1, 2, 3]

接下来手动模拟 flat 这种效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ES5 递归实现
function flatten(arr) {
var result = [];
for (var i = 0, len = arr.length; i < len; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result;
}

// ES6 实现
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}

删除元素

1
2
3
4
var list = [1, 2, 3, 4]
var pos = list.indexOf(1)
list.splice(pos, 1)
console.info(list) // [2, 3, 4]

数组翻转

(二)面试题

1. ["0x1", "0x2", "0x3"].map(parseInt)

正确结果:[1,NaN,0]

首先代码可转换如下:

1
2
3
4
5
6
{
let arr = ["0x1", "0x2", "0x3"].map((item, index) => {
return parseInt(item, index)
})
console.log(arr);
}

parseInt 方法:parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。radix 参数可选,如果 radix 不选或者等于 0 的时候,该方法首先会对 string 方法进行识别是几进制的数,然后转化为十进制的数。

  • 因为 parseInt 可以传递 2 个参数,然后自动的给它按顺序的传递了 item 和 index 的参数。
  • 因此 arr 就等于:arr = [parseInt("0x1", 0), parseInt("0x2", 1), parseInt("0x3", 2)];
1
2
3
4
5
6
7
1.第一个元素:以为radix等于0,所以parseInt就会自动的识别string是不是一个正确的进制数,发现“0x1”是一个16进制的数,所以它会把“0x1”当作一个16进制的数然后再转化为10进制。

2.第二个元素:radix = 1,因为没有不存在1进制的数,所以就直接返回NaN

3.第三个元素:radix = 2,“0x3”当作二进制数然后转化为10进制数,二进制数就只包含01,所以在“0x3”中在满足条件的最前面就只有0,所以二机制0转化为10进制为0.

所以输出结果就是[1,NaN,0].
1
2
parseInt(0x3, 2) // NaN
parseInt('0x3', 2) // 0

2. array.sort()

1
2
3
4
var arr = [-1, 1, 3, 4];
arr.sort((a, b) => {
return Math.abs(a-2)-Math.abs(b-2);
})

3. 递归

1
2
3
4
5
function fun(...dimentions) {
const [layer, ...d] = dimentions;
return d.length ? Array(layer).fill(0).map(_ => fun(...d)) : Array(layer);
}
fun(2,3,4);

image-20220310211637837

解释执行经过:

  • 解释了下解构,layer 的值和 d 的值
  • 解释了 三目运算的执行逻辑
  • Array(layer) 的执行结果,(面试的时候,我把这个第一次的说错,正确的时候返回长度为 layer 的稀松数组,小姐姐提示下,我意识到自己说错了)
  • fill(0),将数组的填充 0,填充多少次,由数组的长度决定,接下来调用 map 函数

为什么需要 fill(0):因为 Array 函数执行完毕后,只是改了数组的长度,元素实际是没有元素的,所以在调用 map 的时候,回调函数是不会执行的。