最近在网上数组去重的方法很多,想了一下无非就是两种
一、两层循环法
let array = [0,1,2,'a','b']
let arr2 = [0,12,'a','abc','d']
console.log(typeof arr2);
/**
* 这里打印数组的类型是object,因为这里创建一个数组的过程是简化的写法,为语法糖。就相当于let arr = new Array
* 判断Array的的时候,我们可以使用 instanceof 来判断
*/
console.log(arr2 instanceof Array);
let newArray = array.concat(arr2);
循环遍历,利用for循环嵌套for循环,然后splice去重(Es5中最常用)
双层循环,外层循环元素,内层循环时比较值。
function remove (arr) {
for(let i = 0;i<arr.length;i++){
for(let j = i+1;j<arr.length;j++){
if(arr[i]===arr[j]){
arr.splice(j,1);
j--;
}
}
}
return arr;
}
remove(newArray);
二、利用语法自身键不可重复性
function remove2(arr){
if(!Array.isArray(arr)){
console.log('type error');
return;
}
var array = [];
for(let i=0;i<arr.length;i++){
if(array.indexOf(arr[i]) === -1){
array.push(arr[i])
}
}
return array;
}
remove(newArray);
新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组
三、利用set去重 (ES6中最常用)
let newArray = new Set(array.concat(arr2));
嘻嘻,用到的都是js中操作数组比较常用的方法
这是一条华丽的分割线
最近看面试题时遇到了几种不同for循环,因为没有深入了解导致做题时无法区分它们的用法,尤其是在以及在使用时的注意点。因此对js中的四种for循环进行了总结区分:
在ES5中,for循环有三种,分别是for/for-in/forEach
在ES6中,新增了一种循环,for-of
一、最简单的for循环(循环代码块一定的次数)
let array = [0,1,2];
for(let i = 0;i<array.length;i++){
console.log(array[i]);
}
// 如果数组长度在循环过程中不会改变,将数组长度用变量存储起来会获得更好的效率
let arr = ['a','b','c'];
for(let i = 0, long = arr.length; i<long; i++){
console.log(arr[i]);
}
二、for-in(循环遍历对象的属性)
for(let index in arr){
console.log("arr["+index+"]=" + arr[index]);
}
// for-in 循环遍历的是对象的属性,而不是数组的索引。因此, for-in 遍历的对象不局限于数组,还可以遍历对象。
// 而且for-in 不仅仅遍历 array 自身的属性,其还遍历 array 原型链上的所有可枚举的属性
const person = {
name: 'jack',
like: 'basketball'
}
person.age = 18;
for(let i in person){
console.log("person["+i+"]=" + person[i]);
}
// 需要注意的是,for-in遍历属性的顺序并不确定,即输出的结果顺序与属性在对象中的顺序无关,与任何顺序都无关。
// 其实for-in并不适合用来遍历Array中的元素,其更适合遍历对象中的属性,这也是其被创造出来的初衷。
三、forEach
array.forEach((data,index,arr) =>{
console.log("arr["+index+"] =" + data);
})
/**
* forEach 方法为数组中含有有效值的每一项执行一次 callback 函数,那些已删除(使用 delete 方法等情况)或者从未赋值的项将被跳过(不包括那些值为 undefined 或 null 的项)。 callback 函数会被依次传入三个参数:
数组当前项的值;
数组当前项的索引;
数组对象本身;
添加数组当前项的索引参数,注意callback 函数中的三个参数顺序是固定的,不可以调整。
*/
// forEach 遍历的范围在第一次调用callback前就会确定。调用forEach后添加到数组中的项不会被callback访问到。
const arr2 = [];
arr2[0] = "a";
arr2[5] = "b";
arr2[10] = "c";
arr2.word = "HelloWorld";
arr2.forEach((data, index, array) => {
console.log(data, index, array);
});
四、for-of(ES6新增循环)
- 为什么新引入一个for-of循环
在ES6之前:forEach 不能 break 和 return;
for-in 缺点更加明显,它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。 - for-of可以干什么
跟 forEach 相比,可以正确响应 break, continue, return。
for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。
for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。
嘻嘻,这还是一条分割线
最后纪录一个最近看到的涉及forEach和for-of的大厂面试题:
输出以下代码运行结果,为什么?如果希望每隔 1s 输出一个结果,应该如何改造?注意不可改动 square 方法。
const list = [1, 2, 3]
const square = num => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
function test() {
list.forEach(async x=> {
const res = await square(x);
console.log(res);
})
}
test()
forEach是不能阻塞的,默认是请求并行发起,所以是同时输出1、4、9
// 方法一、把forEach换成普通for循环
async function test() {
for(let i = 0, long= list.length; i<long; i++){
const res = await square(list[i])
console.log(res);
}
}
// 方法二、更换为for-of循环
async function test() {
for(x of list){
const res = await square(x);
console.log(res);
}
}
// 方法三、利用promise本身的链式调用
function test() {
let promise = Promise.resolve();
list.forEach(x => {
promise = promise.then(() => square(x)).then((res) => {
console.log(res);
})
})
}