前言
本文温习前端开发中常见的3种数据结构:数组Array、有序集合Set、有序映射Map,并分别介绍其特性、用法示例
数组-Array
适用于存放和读取有序集合、不要求集合元素唯一性时;可通过索引快速访问元素,实现元素增删改查时
使用方法
1.创建、修改
// 直接创建
let arr = [6,7,8,9,0];
// 使用构造函数创建
// arr = new Array(6,7,8,9,0);
console.log('len:',arr.length); // len: 5
// 得到数组长度要用length而不是size
console.log('size:',arr.size) // size: undefined
console.log(arr.at(0)); // 6
console.log(arr[0]); // 6
arr[0] = 100; // 修改指定位置的元素
console.log(arr); // [ 100, 7, 8, 9, 0 ]
// fill(x,start?,end?) 从start索引开始填充元素x,end可选,end指定的位置不包含
console.log(arr.fill(9999,2,4)); // [ 100, 7, 9999, 9999, 0 ]
// 所有元素填充为 -1
arr.fill(-1,0);
console.log(arr); // [ -1, -1, -1, -1, -1 ]
2.添加删除元素
push添加,pop尾部删除,shift头部删除,unshift头部插入,concat合并数组
let arr = [ 6, 7, 8, 9, 0 ]
// push(x)尾部添加指定元素
let len = arr.push(10);
console.log("len:",len); // len: 6
// pop() 尾部删除一个元素,并返回这个元素,若数组为空,返回undefined
let lastVal = arr.pop();
console.log("lastVal:",lastVal,", len:",arr.length); // lastVal: 10 , len: 5
// shift() 头部删除一个元素,返回被删除元素,若数组为空返回undefined
let delHeadVal = arr.shift();
console.log("delHeadVal:",delHeadVal,",len:",arr.length); // delHeadVal: 6 ,len: 4
// unshift(x1,x2,x3...)在元素开头插入一个或多个元素,返回插入后的数组长度
len = arr.unshift(88);
console.log("len:",len); // len: 5
let arr2 = [2,4,6];
// concat合并两个或多个数组,返回新的数组,注意arr不会发生改变
let ansArr = arr.concat(arr2);
console.log("ansArr len:",ansArr.length); // ansArr len: 8
slice拷贝数组元素
let nums = [1, 2, 3, 4, 5, 6, 7];
// slice(start,exclusiveEnd) 包括start,不包括exclusiveEnd,返回一个新数组对象,该方法不会改变原始数组
let sliceNums = nums.slice(1, 3);
console.log("sliceNums:", sliceNums); // sliceNums: [ 2, 3 ]
sliceNums[0] = 888;
// 修改sliceNums,不会改变原始数组nums
console.log("nums:", nums); // nums: [ 1, 2, 3, 4, 5, 6, 7 ]
// 从索引5开始提取所有的元素
console.log(nums.slice(4)); // [5, 6, 7 ]
// 易错提示:-1表示倒数第1个元素,由于exclusiveEnd不包括,因此得到的最后一个元素是倒数第2个元素
console.log(nums.slice(5, -1)); // [ 6 ]
// slice() 提取所有元素
console.log(nums.slice()); // [ 1, 2, 3, 4, 5, 6, 7 ]
// slice 超出索引范围,返回一个空数组 []
console.log(nums.slice(88)); // []
splice修改、删除、插入元素
let nums = [1, 2, 3, 4, 5, 6, 7];
// 删除指定位置的N个元素: nums.splice(index,count) 从index位置开始连续删除count个元素,并返回删除的元素,nums本身被改变
let removedNums = nums.splice(2, 5);
console.log("removedNums:", removedNums, ", nums:", nums); // removedNums: [ 3, 4, 5, 6, 7 ] , nums: [ 1, 2 ]
// 元素替换:从索引1处删除0个元素(返回为空),并对原始数组添加多个元素,原始数组被改变
console.log("res:",nums.splice(1, 0, 'x', 'y', 'z')); // res: []
console.log("splice nums:", nums); // splice nums: [ 1, 'x', 'y', 'z', 2 ]
// 删除后续元素:删除从索引2开始的所有元素,返回被删除元素
console.log("removed: ", nums.splice(2), "nums:", nums); // removed: [ 'y', 'z', 2 ] nums: [ 1, 'x' ]
nums = [1, 2, 3, 4];
// 删除索引2处的元素
console.log("del: ", nums.splice(2, 1),", nums:",nums); // del: [ 3 ] , nums: [ 1, 2, 4 ]
// 将索引2处的1个元素删除,并替换为78,88
nums.splice(2, 1, 78, 88);
console.log("nums:", nums); // nums: [ 1, 2, 78, 88 ]
3.筛选、查找元素
filter筛选,find查找元素,findIndex、indexOf、lastIndexOf查找元素索引,includes判断是否包含
let nums = [1, 2, 3, 4, 5, 6, 7];
// filter过滤出符合指定规则的元素
let evenNums = nums.filter(x => x % 2 == 0);
// find查找数组中满足测试函数的第一个元素的值,若没有符合的,返回undefined
console.log("evenNums:", evenNums); // evenNums: [ 2, 4, 6 ]
let targetValue = nums.find(x => x > 5);
console.log("targetvalue:", targetValue); // targetvalue: 6
// findIndex返回符合测试元素的第一个索引,找不到返回-1
let targetIndex = nums.findIndex(x => x > 5);
console.log("targetIndex:", targetIndex); //targetIndex: 5
// indexOf(x)从索引位置0开始寻找一个指定的元素,找到则返回指向第一个元素的索引,找不到则返回-1
console.log("index:", nums.indexOf(5)); // index: 4
// indexOf(x,fromIndex) 从index位置寻找一个元素x
console.log(nums.indexOf(1, 5)); // -1
// lastIndexOf(x,fromIndex) 若fromIndex缺省,默认从最后一个位置开始向前查找x元素的索引
console.log(nums.lastIndexOf(5)) // 4
// includes(x)判断数组中是否包含一个指定元素,若包含返回true,否则false
console.log(nums.includes(4)); // true
// includes(x,fromIndex) 从fromIndex位置开始查找是否包含指定元素
console.log(nums.includes(2, 1)); // true
4.成分判断
every测试所有都符合、some测试至少一个元素符合
let nums = [5, 8, 4, 6, 7];
// every 判断所有元素是否都通过测试函数,若是返回true,否则false
let isAllEven = nums.every(x => x % 2 == 0);
console.log("isAllEven:", isAllEven); // false
// some 判断是否至少有一个元素通过测试函数,若是返回true
let hasEven = nums.some(x => x % 2 == 0);
console.log("hasEven:", hasEven); // true
5.反转、排序
reverse反转数组顺序,sort排序
let ids = new Array(2, 0, 3, 5, 1, 4);
// reverse反转数组元素顺序,并返回数组本身
let reverseIds = ids.reverse();
console.log("reverseIds:", reverseIds); // reverseIds: [ 4, 1, 5, 3, 0, 2 ]
console.log("ids:", ids); // ids: [ 4, 1, 5, 3, 0, 2 ]
// sort排序会改变原始数组顺序,并返回数组本身,默认从小到大排序
console.log(ids.sort()); // [ 0, 1, 2, 3, 4, 5 ]
// 自定义排序,a-b升序,b-a降序
ids.sort((a,b)=>b-a);
console.log(ids);// [ 5, 4, 3, 2, 1, 0 ]
6.元素遍历
for…in遍历索引,for…of遍历元素, forEach遍历元素及索引
let nums = [5, 8, 4, 6, 7];
// for...in遍历数组,遍历的是索引
for (let index in nums) {
console.log("index:", index, ", value:", nums[index]);
}
// for...of遍历数组,遍历得到的是具体元素
for (let x of nums) {
console.log("x=", x);
}
// forEach遍历元素
nums.forEach(x => {
console.log("x:", x);
});
// forEach两个参数时第2个元素是索引
nums.forEach((x, index) => {
console.log("x:", x, ", index:", index);
});
// 普通遍历方法
for (let i = 0; i < nums.length; i++) {
console.log("nusm:", nums[i]);
}
7.累计、分组运算
reduce对数字元素进行算术累计运算,对字符串连接运算
let nums = [1, 2, 3];
let initVal = 0;
// reduce对数组中每个元素执行一个函数,将结果汇总为单个值返回,initVal指定初始值,preVal+curVal表示将数组元素累加
let sums = nums.reduce((preVal, curVal) => preVal + curVal, initVal);
console.log("sums:", sums); // 6
const words = ['Hello', 'World', 'JavaScript'];
// reduce将元素连接为一个字符串
const sentence = words.reduce((accumulator, currentValue) => accumulator + ' ' + currentValue);
console.log("sentence:"+sentence); // sentence:Hello World JavaScript
// join将所有元素用字符-连接为一个字符串
let str = nums.join('-');
console.log(str); // 1-2-3
reduce分组
const users = [
{name:"User-A",age:21},
{name:'User-B',age:20},
{name:'User-C',age:20},
{name:'User-E',age:20}];
let initVal = {};
let ageGroup = users.reduce((preVal,curVal)=>{
let age = curVal.age;
if(!preVal[age]){
preVal[age] = [];
}
preVal[age].push(curVal);
return preVal;
},initVal);
console.log(ageGroup);
// {
// '20': [
// { name: 'User-B', age: 20 },
// { name: 'User-C', age: 20 },
// { name: 'User-E', age: 20 }
// ],
// '21': [ { name: 'User-A', age: 21 } ]
// }
8.变换
map、flatMap将每个元素进行变换
let nums = [1, 2, 3];
console.log(nums.map(x=>'id-'+x)); // [ 'id-1', 'id-2', 'id-3' ]
// map:对数组中的每个元素执行回调函数,回调函数的返回值作为元素
let ans = nums.map(x=>[x*2]);
console.log(ans); // [ [ 2 ], [ 4 ], [ 6 ] ]
// flatMap: 对每个元素执行回调函数,并将结果展平一层后返回一个新数组,相当于先执行 map,然后再执行 flat
let ans2 = nums.flatMap(x=>[x*2]);
console.log(ans2); // [ 2, 4, 6 ]
let nums2 = nums.map(x=>[x,x*x]);
console.log(nums2); // [ [ 1, 1 ], [ 2, 4 ], [ 3, 9 ] ]
let ans3 = nums.flatMap(x=>[x,x*x]);
console.log(ans3); // [ 1, 1, 2, 4, 3, 9 ]
有序集合-Set
用于存储具有唯一性的元素,常用于元素查找、去重场景
使用方法
1.元素去重
普通类型插入元素唯一,可用于元素去重,对象插入是否唯一根据引用判断
let arr = [1,2,2,3,1];
// 数组转为集合去重
let setNums = new Set(arr);
// 集合转为数组
console.log(Array.from(setNums)); // [ 1, 2, 3 ]
let set = new Set();
set.add(3);
set.add(2);
// 2已经存在,所以不会被重复添加
set.add(2);
// 获取集合大小要使用set.size,而不是set.length(输出为undefined)
console.log("size:", set.size); // size: 2
2.元素存在
可以在内部表示为哈希表,查找时间复杂度为O(1)
// set中是否含有某个元素
let isContain = set.has(4);
console.log("isContain:", isContain); // false
3.有序遍历
元素遍历时的顺序保持与插入顺序一致,可使用forEach或 for…of遍历
let set = new Set([5,8,7,6]);
// set集合的遍历应该使用 for...of而不是for...in,以下遍历什么也不会输出:
// for(let id in set){
// console.log(id);
// }
// 遍历方法1:使用 for...of遍历
for (let id of set) {
console.log(id);
}
// 遍历方法2:使用forEach
set.forEach(id => {
console.log("id:", id);
});
// 获取迭代器对象,set.values()迭代器遍历,仍然要使用 for...of而不是for...in
for (let value of set.values()) {
console.log("value:", value);
}
//输出的 key和value其实是一样的
for (let [key, value] of set.entries()) {
console.log("key:", key, "value:", value);
}
4.删除操作
delete删除某个元素,clear清空集合
// delete 删除集合中数字5,成功返回true,否则false
let isDel = set.delete(5);
console.log("isDel:", isDel);
set.clear();
有序映射-Map
Map 适合于频繁增删改键值对的场景,可确保的键唯一性,内部实现用的是哈希表,具有较好性能
使用方法
1.键值添加、更改
新增或修改set,取值get,判断存在has,删除delete
let map = new Map();
// set进行键值添加
map.set("name", "Alice");
map.set("age", 24);
// 键已存在则会被更新
map.set("name", "Blob");
// 注意:Map不要使用[]操作符,这种设置属性的方式不会改变 Map 的数据结构,它使用的是通用对象的特性,因此使用has是查不到的
// ap['gender'] = 'female';
// console.log('! has:', map.has('gender')); // false
console.log(map.has('name')); // true
console.log(Array.from(map.entries())); // [ [ 'name', 'lisi' ], [ 1, 'apple' ] ]
// 取与键关联的值,若不存在则返回undefined
let value = map.get("name");
console.log("value:", value); // value: Blob
// 获取map大小使用size,而不是length
console.log(map.size); // 2
map.clear();
2.有序遍历
Map 的键是有序的,遍历输出保持与添加时的顺序一致
Map遍历for…of或forEach
// 使用可迭代对象初始化Map
let userMap = new Map([['id', 23], ['name', 'zhangsan'], ['age', 18]]);
console.log(userMap);
// for...of 遍历Map
for (const [key, value] of userMap) {
console.log("key:", key, ",value:", value);
}
// 或者
for (const [key, value] of userMap.entries()) {
console.log("key#:", key, ",value#:", value);
}
// 或者
for (let entry of userMap) {
console.log("entry:", entry);
// entry[0]为键,entry[1]为值
// console.log(entry[0],entry[1])
}
// forEach遍历map
userMap.forEach((value, key) => {
console.log("value=", value, ",key=", key);
});
for (const key of userMap.keys()) {
console.log("key:", key);
}
for (const value of userMap.values()) {
console.log('value:', value);
}
关于Map映射与普通Object对象的区别
1.键的有序性:
Map
中的键以简单、直接的方式排序,Map
对象按照插入的顺序迭代条目、键和值
Object
的键排序比较复杂的,最好不要依赖属性的顺序
2.键的类型:
Map 的键可以为任何值(包括函数、对象或任何原始值)
Object 的键必须为 String 或 Symbol
3.大小计算:
Map
中的项目数量确定很容易从其 size 属性中获得
Object 中的项目数量确定比较麻烦,效率也较低,一种常见的方法是通过获取 Object.keys() 返回的数组的长度
4.其他方面:
Map
性能在涉及频繁添加和删除键值对的场景中表现更好,而Object
未针对频繁添加和删除键值对进行优化
关于普通Object用法
// 普通对象
// 添加或修改属性obj[property],或obj.property,
// 遍历for...in(遍历key), for...of(遍历entries)
function testObject2() {
// 创建一个空对象
let obj = {};
obj[2] = 'zhangsan';
obj[1] = 'lisi';
console.log(obj);
obj.msg = "hello";
console.log(obj);
// 判断对象是否有某个属性
console.log(obj.hasOwnProperty("msg")); // true
console.log('msg' in obj); // true
for (let key in obj) {
console.log("key:", key, "value:", obj[key]);
}
// 遍历键值对
Object.entries(obj).forEach((key, value) => {
console.log("key:", key, ", value:", value);
});
for (const [key, value] of Object.entries(obj)) {
console.log("key=", key, ", value=", value);
}
// 类似:Object.values(obj);
Object.keys(obj).forEach(key => {
console.log("key=", key);
});
// delete删除某个属性,删除后再访问该属性为undefined
delete obj.msg;
console.log(obj); // { '1': 'lisi', '2': 'zhangsan' }
console.log(obj.msg); // undefined
// 键的个数
console.log(Object.keys(obj).length); // 2
}
参考资料
1.JavaScript 标准内置对象
2.JavaScript数据类型