先来看一个题目:
var arr = [
function () {
return 0;
},
function () {
return 1;
},
function () {
return 2;
},
];
var set = new Set([
function () {
return 1;
},
function () {
return 2;
},
function () {
return 3;
},
]);
arr.forEach((fn, i, ar) => {
console.log(fn());
ar.push(null);
});
set.forEach((fn, i, se) => {
console.log(fn());
se.add(null);
});
通过以上示例的执行结果可以知道:
Array使用forEach方法进行遍历只遍历了最初数组的长度Set的遍历顺序是其添加的顺序Set使用forEach方法进行遍历则会遍历到后添加进去的值
可以推测 Array.prototype.forEach 的实现大致是:
Array.prototype.myForEach = function(cb, context) {
// 即在首次调用 forEach 回调之前就已经确定了遍历的长度
const length = this.length
for (let i = 0; i < length; i++) {
cb.apply(context, [this[i], i, this)
}
}
同样推测 Set.prototype.forEach 的实现大致是:
Set.prototype.myForEach = function (cb, context) {
// 推测是通过迭代器实现的
const iterator = this[Symbol.iterator]();
let next;
let index = 0;
while ((next = iterator.next()) && !next.done) {
cb.apply(context, [next.value, index, this]);
index++;
}
};
其它的一些区别
Set是无序的,它的遍历顺序和添加顺序相同Set中的值是唯一的,其判断基于===,对于NaN基于isNaN方法Set查找、插入、删除值比Array更快Set可以更方便得查找NaN
Set 和 Array 一些操作的性能测试:
const arr = [];
const set = new Set();
const n = 1000000;
for (let i = 0; i < n; i++) {
arr.push(i);
set.add(i);
}
// 查找
console.time("Array find");
arr.includes(131568);
console.timeEnd("Array find");
console.time("Set find");
set.has(131568);
console.timeEnd("Set find");
// 添加元素
console.time("Array push");
arr.push(n);
console.timeEnd("Array push");
console.time("Array insert");
arr.splice(23568, 0, n);
console.timeEnd("Array insert");
console.time("Set insert");
set.add(n);
console.timeEnd("Set insert");
// 删除元素
console.time("Array delete");
arr.splice(23568, 1);
console.timeEnd("Array delete");
console.time("Set delete");
set.delete(23568);
console.timeEnd("Set delete");
测试结果:
Array find: 0.122ms
Set find: 0.005ms
Array push: 0.001ms
Array insert: 1.082ms
Set insert: 0.003ms
Array delete: 0.671ms
Set delete: 0.004ms
从结果看:Set 在查找、删除和随机插入性能远好于 Array
所以在存储无序数据的场景下,应该优先选择 Set
使用场景
- 利用唯一性可以做数组、字符串去重
- 存储无序数据