# 一.确保数组的长度

let array = Array(5).fill("");
console.log(array); // 输出(5)["", "", "", "", ""]
1
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"]
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));
1
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));
1
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]
1
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]
1
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));
1
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));
1
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]
1
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"]
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"]
1
2
3

第二种

const uniqueWithSpreadOperator = [...new Set(cars)];
console.log(uniqueWithSpreadOperator);
// 输出 ["Mazda", "Ford", "Renault", "Opel"]
1
2
3

第三种

function unique(arr) {
  const seen = new Map();
  return arr.filter((a) => !seen.has(a) && seen.set(a, 1));
}
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
1
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,
];
1
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
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]
1
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"]
1
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]);
}
1
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]
1
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]
1
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];
1
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];
1
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
1
2
3

# 九.数组元素的洗牌

对于数组元素的洗牌,不需要使用任何外部的库,比如 Lodash,只要这样做:

var list = [1, 2, 3];
console.log(
  list.sort(function() {
    Math.random() - 0.5;
  })
); // [2,1,3]
1
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);
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]
1
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]
1
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)));
1
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);
1
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;
1
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
1
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}`);
}
1
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
1
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
1
2
3
4
5
6
7
8
9

结果

# 参考

  1. https://mp.weixin.qq.com/s/fpxZQQNjgPGDVI4i-eCXjQ
  2. https://mp.weixin.qq.com/s/YkYvZDbHb77_ImbBe3JbrQ