# 一.确保数组的长度
let array = Array(5).fill("");
console.log(array); // 输出(5)["", "", "", "", ""]
2
# 二. 数组去重
# 1.原始方法(双层循环);兼容性好。
var array = [1, 1, "1", "1"];
function unique(array) {
// res用来存储结果
var res = [];
for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
for (var j = 0, resLen = res.length; j < resLen; j++) {
if (array[i] === res[j]) {
break;
}
}
// 如果array[i]是唯一的,那么执行完循环,j等于resLen
if (j === resLen) {
res.push(array[i]);
}
}
return res;
}
console.log(unique(array)); // [1, "1"]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2.indexOf
var array = [1, 1, "1"];
function unique(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
if (res.indexOf(current) === -1) {
res.push(current);
}
}
return res;
}
console.log(unique(array));
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.排序后去重
var array = [1, 1, "1"];
function unique(array) {
var res = [];
var sortedArray = array.concat().sort();
var seen;
for (var i = 0, len = sortedArray.length; i < len; i++) {
// 如果是第一个元素或者相邻的元素不相同
if (!i || seen !== sortedArray[i]) {
res.push(sortedArray[i]);
}
seen = sortedArray[i];
}
return res;
}
console.log(unique(array));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4.封装 unique API
第一版:根据一个参数 isSorted 判断传入的数组是否是已排序的,如果为 true,我们就判断相邻元素是否相同,如果为 false,我们就使用 indexOf 进行判断
var array1 = [1, 2, "1", 2, 1];
var array2 = [1, 1, "1", 2, 2];
// 第一版
function unique(array, isSorted) {
var res = [];
var seen = [];
for (var i = 0, len = array.length; i < len; i++) {
var value = array[i];
if (isSorted) {
if (!i || seen !== value) {
res.push(value);
}
seen = value;
} else if (res.indexOf(value) === -1) {
res.push(value);
}
}
return res;
}
console.log(unique(array1)); // [1, 2, "1"]
console.log(unique(array2, true)); // [1, "1", 2]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
第二版:字母的大小写视为一致,比如'a'和'A',保留一个就可以了!
function unique(array, isSorted, iteratee) {
var res = [];
var seen = [];
for (var i = 0, len = array.length; i < len; i++) {
var value = array[i];
var computed = iteratee ? iteratee(value, i, array) : value;
if (isSorted) {
if (!i || seen !== value) {
res.push(value);
}
seen = value;
} else if (iteratee) {
if (seen.indexOf(computed) === -1) {
seen.push(computed);
res.push(value);
}
} else if (res.indexOf(value) === -1) {
res.push(value);
}
}
return res;
}
console.log(
unique(array3, false, function(item) {
return typeof item == "string" ? item.toLowerCase() : item;
})
); // [1, "a", 2]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
参考:underscore 的 unique 函数 (opens new window)。
# 5.ES5,filter
var array = [1, 2, 1, 1, "1"];
function unique(array) {
var res = array.filter(function(item, index, array) {
// 这利用indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
// let test1=array.indexOf(item)
// let test= test1=== index;
return array.indexOf(item) === index;
});
return res;
}
console.log(unique(array));
2
3
4
5
6
7
8
9
10
11
12
13
排序去重的方法:
var array = [1, 2, 1, 1, "1"];
function unique(array) {
return array
.concat()
.sort()
.filter(function(item, index, array) {
return !index || item !== array[index - 1];
});
}
console.log(unique(array));
2
3
4
5
6
7
8
9
10
11
12
# 6. Object 键值对
var array = [1, 2, 1, 1, "1"];
function unique(array) {
var obj = {};
return array.filter(function(item, index, array) {
return obj.hasOwnProperty(item) ? false : (obj[item] = true);
});
}
console.log(unique(array)); // [1, 2]
2
3
4
5
6
7
8
9
10
我们可以发现,是有问题的,因为 1 和 '1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以使用 typeof item + item 拼成字符串作为 key 值来避免这个问题:
var array = [1, 2, 1, 1, "1"];
function unique(array) {
var obj = {};
return array.filter(function(item, index, array) {
return obj.hasOwnProperty(typeof item + item)
? false
: (obj[typeof item + item] = true);
});
}
console.log(unique(array)); // [1, 2, "1"]
2
3
4
5
6
7
8
9
10
11
12
# 7.ES6 ;适合处理基本类型的数组.
第一种:
const cars = ["Mazda", "Ford", "Renault", "Opel", "Mazda"];
const uniqueWithArrayFrom = Array.from(new Set(cars));
console.log(uniqueWithArrayFrom); // 输出 ["Mazda", "Ford", "Renault", "Opel"]
2
3
第二种
const uniqueWithSpreadOperator = [...new Set(cars)];
console.log(uniqueWithSpreadOperator);
// 输出 ["Mazda", "Ford", "Renault", "Opel"]
2
3
第三种
function unique(arr) {
const seen = new Map();
return arr.filter((a) => !seen.has(a) && seen.set(a, 1));
}
2
3
4
# 8.特殊类型比较
去重的方法就到此结束了,然而要去重的元素类型可能是多种多样,除了例子中简单的 1 和 '1' 之外,其实还有 null、undefined、NaN、对象等,那么对于这些元素,之前的这些方法的去重结果又是怎样呢?
在此之前,先让我们先看几个例子:
var str1 = "1";
var str2 = new String("1");
console.log(str1 == str2); // true
console.log(str1 === str2); // false
console.log(null == null); // true
console.log(null === null); // true
console.log(undefined == undefined); // true
console.log(undefined === undefined); // true
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(/a/ == /a/); // false
console.log(/a/ === /a/); // false
console.log({} == {}); // false
console.log({} === {}); // false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
那么,对于这样一个数组
var array = [
1,
1,
"1",
"1",
null,
null,
undefined,
undefined,
new String("1"),
new String("1"),
/a/,
/a/,
NaN,
NaN,
];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
以上各种方法去重的结果到底是什么样的呢?
我特地整理了一个列表,我们重点关注下对象和 NaN 的去重情况:
| 方法 | 结果 | 说明 |
|---|---|---|
| for循环 | [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] | 对象和 NaN 不去重 |
| indexOf | [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] | 对象和 NaN 不去重 |
| sort | [/a/, /a/, "1", 1, String, 1, String, NaN, NaN, null, undefined] | 对象和 NaN 不去重 数字 1 也不去重 |
| filter + indexOf | [1, "1", null, undefined, String, String, /a/, /a/] | 对象不去重 NaN 会被忽略掉 |
| filter + sort | [/a/, /a/, "1", 1, String, 1, String, NaN, NaN, null, undefined] | 对象和 NaN 不去重 数字 1 不去重 |
| 优化后的键值对方法 | [1, "1", null, undefined, String, /a/, NaN] | 全部去重 |
| Set | [1, "1", null, undefined, String, String, /a/, /a/, NaN] | 对象不去重 NaN 去重 |
想了解为什么会出现以上的结果,看两个 demo 便能明白:
// demo1
var arr = [1, 2, NaN];
arr.indexOf(NaN); // -1
2
3
indexOf 底层还是使用 === 进行判断,因为 NaN ==== NaN 的结果为 false,所以使用 indexOf 查找不到 NaN 元素
// demo2
function unique(array) {
return Array.from(new Set(array));
}
console.log(unique([NaN, NaN])); // [NaN]
2
3
4
5
Set 认为尽管 NaN === NaN 为 false,但是这两个元素是重复的。
# 三. 数组映射(不使用 Array.map)
你知道这里有另外一种方法可以实现数组映射,而不使用 Array.map 吗?如果不知道,请继续往下看。
const cities = [
{ name: "Paris", visited: "no" },
{ name: "Lyon", visited: "no" },
{ name: "Marseille", visited: "yes" },
{ name: "Rome", visited: "yes" },
{ name: "Milan", visited: "no" },
{ name: "Palermo", visited: "yes" },
{ name: "Genoa", visited: "yes" },
{ name: "Berlin", visited: "no" },
{ name: "Hamburg", visited: "yes" },
{ name: "New York", visited: "yes" },
];
const cityNames = Array.from(cities, ({ name }) => name);
console.log(cityNames);
// 输出 ["Paris", "Lyon", "Marseille", "Rome", "Milan", "Palermo", "Genoa", "Berlin", "Hamburg", "New York"]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 四.在循环中缓存 array.length
处理的是一个大的数组
var length = array.length;
for (var i = 0; i < length; i++) {
console.log(array[i]);
}
2
3
4
# 五.获取数组中最后一个元素
Array.prototype.slice(begin,end)用来获取 begin 和 end 之间的数组元素。如果你不设置 end 参数,将会将数组的默认长度值当作 end 值。但有些同学可能不知道这个函数还可以接受负值作为参数。如果你设置一个负值作为 begin 的值,那么你可以获取数组的最后一个元素。如:
var array = [1, 2, 3, 4, 5, 6];
console.log(array.slice(-1)); // [6]
console.log(array.slice(-2)); // [5,6]
console.log(array.slice(-3)); // [4,5,6]
2
3
4
# 六.数组截断
这个小技巧主要用来锁定数组的大小,如果用于删除数组中的一些元素来说,是非常有用的。例如,你的数组有 10 个元素,但你只想只要前五个元素,那么你可以通过 array.length=5 来截断数组。如下面这个示例:
var array = [1, 2, 3, 4, 5, 6];
console.log(array.length); // 6
array.length = 3;
console.log(array.length); // 3
console.log(array); // [1,2,3]
2
3
4
5
# 七.合并数组
如果你要合并两个数组,一般情况之下你都会使用 Array.concat()函数:
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.concat(array2)); // [1,2,3,4,5,6];
2
3
然后这个函数并不适合用来合并两个大型的数组,因为其将消耗大量的内存来存储新创建的数组。在这种情况之个,可以使用 Array.push().apply(arr1,arr2)来替代创建一个新数组。这种方法不是用来创建一个新的数组,其只是将第一个第二个数组合并在一起,同时减少内存的使用:
var array1 = [1, 2, 3];
var array2 = [4, 5, 6];
console.log(array1.push.apply(array1, array2)); // [1,2,3,4,5,6];
2
3
# 八.将 NodeList 转换成数组
如果你运行 document.querySelectorAll(“p”)函数时,它可能返回 DOM 元素的数组,也就是 NodeList 对象。但这个对象不具有数组的函数功能,比如 sort()、reduce()、map()、filter()等。为了让这些原生的数组函数功能也能用于其上面,需要将节点列表转换成数组。可以使用[].slice.call(elements)来实现:
var elements = document.querySelectorAll("p"); // NodeList
var arrayElements = [].slice.call(elements); // Now the NodeList is an array
var arrayElements = Array.from(elements); // This is another way of converting NodeList to Array
2
3
# 九.数组元素的洗牌
对于数组元素的洗牌,不需要使用任何外部的库,比如 Lodash,只要这样做:
var list = [1, 2, 3];
console.log(
list.sort(function() {
Math.random() - 0.5;
})
); // [2,1,3]
2
3
4
5
6
# 十一.一句代码生成一个[1-100]的数组
// 方式1
const arr1 = [...Array(100).keys()]; //生成的是0-99
// 方式2
const arr2 = Array.from(Array(100), (e, i) => i + 1);
2
3
4
# MDN 解释:在 Array.from 中使用箭头函数
// Generate a sequence of numbers
// Since the array is initialized with `undefined` on each position,
// the value of `v` below will be `undefined`
Array.from({ length: 5 }, (v, i) => i);
// [0, 1, 2, 3, 4]
2
3
4
5
参考链接
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/keys
# 十二.一句代码实现数组去重
const arr = [1, 2, 2, 3, 4, 5, 5, 3];
//方式1:
const newArr1 = [...new Set(arr)]; //输出:类数组[1, 2, 3, 4, 5]
//方式2
const newArr2 = arr.reduce(
(prev, cur) => (prev.includes(cur) ? prev : [...prev, cur]),
[]
); //类数组[1, 2, 3, 4, 5]
2
3
4
5
6
7
8
参考链接
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
# 十三.一句代码求两个数组交集和差集
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
let intersect = new Set([...a].filter((x) => b.has(x)));
let difference = new Set([...a].filter((x) => !b.has(x)));
2
3
4
参考链接
https://es6.ruanyifeng.com/#docs/set-map
# 十四.一句代码获取数组最大值和最小值
let numbers = [1, 3, 5, 5, 6, -3, 10];
let max = Math.max(...numbers);
let min = Math.min(...numbers);
2
3
参考链接
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/max
# 十五.删除数组最后两个元素
let a = [1, 2, 4, 5];
a.length = a.length - 2;
2
# 十六.通过 for-of 循环来遍历数组元素和索引
方法 forEach() 允许你遍历一个数组的元素和索引:
var arr = ["a", "b", "c"];
arr.forEach(function(elem, index) {
console.log("index = " + index + ", elem = " + elem);
});
// Output:
// index = 0, elem = a
// index = 1, elem = b
// index = 2, elem = c
2
3
4
5
6
7
8
ES6 的 for-of 循环支持 ES6 迭代(通过 iterables 和 iterators)和解构。如果你通过数组的新方法 enteries() 再结合解构,可以达到上面 forEach 同样的效果:
const arr = ["a", "b", "c"];
for (const [index, elem] of arr.entries()) {
console.log(`index = ${index}, elem = ${elem}`);
}
2
3
4
arr.enteries() 通过索引-元素配对返回一个可迭代对象。然后通过解构数组 [index, elem] 直接得到每一对元素和索引。console.log() 的参数是 ES6 中的模板字面量特性,这个特性带给字符串解析模板变量的能力。
# 十七.展开操作符 (...) 将它的操作对象展开并插入数组。
通过变量解构交换两个变量的值 如果你将一对变量放入一个数组,然后将数组解构赋值相同的变量(顺序不同),你就可以不依赖中间变量交换两个变量的值:
[a, b] = [b, a];
# 十八.代码输出结果
# (1)
const [{ a: b, b: a }, c, d = a] = [{ a: 1, b: 2 }, 1];
console.log(a, b, c, d); //2 1 1 2
2
# (2)代码执行后,arr 的值
var arr = [];
for (var i = 1; i < 5; i++) {
(function() {
setTimeout(function() {
arr.push(i);
}, 0);
})();
}
arr.push(0); //0 5 5 5 5
2
3
4
5
6
7
8
9
结果
# 参考
- https://mp.weixin.qq.com/s/fpxZQQNjgPGDVI4i-eCXjQ
- https://mp.weixin.qq.com/s/YkYvZDbHb77_ImbBe3JbrQ