1.解构赋值
它就是一种能让你从数组或对象中快速提取数据,并赋值给变量的简洁语法,不用再通过下标或属性名一个个取值
传统写法:
let arr = [1,2,3]
let a = arr[0]
let b = arr[1]
let c = arr[3]
console.log(a,b,c)
普通赋值:你拿到一个包裹(数组 / 对象),要先打开外层,再逐个拿出里面的物品(数据),比如 let a = arr[0]; let b = arr[1];
解构赋值写法
let [a,b,c] = [1,2,3]
console.log(a,b,c)
直接定制一个和包裹内部结构匹配的 “取物框”,一次性把对应物品拿出来,比如 let [a, b] = arr
对象解构(适用于无序的键值对集合,按属性名匹配):
// 普通方式取值
const user = { name: "张三", age: 20 };
let userName = user.name;
let userAge = user.age;
console.log(userName, userAge); // 输出:张三 20
// 解构赋值方式
const { name, age } = user; // 属性名要和对象里的一致
console.log(name, age); // 输出:张三 20
// 进阶用法:重命名、设置默认值
const { name: uname, gender = "男" } = user; // 把name重命名为uname,gender设默认值
console.log(uname, gender); // 输出:张三 男
函数参数解构:接收对象参数时直接提取需要的属性
function printUser({ name, age }) {
console.log(`姓名:${name},年龄:${age}`);
}
printUser({ name: "李四", age: 25 }); // 输出:姓名:李四,年龄:25
交换变量:不用临时变量就能交换两个值
let a = 1, b = 2;
[a, b] = [b, a]; // 交换a和b
console.log(a, b); // 输出:2 1
//内层数组的结构也完全匹配,所有变量都能取到对应值。
let [a,b,[c,d]] = [1,2,[3,4]]
console.log(a,b,c,d)
//1 2 3 4
//内层数组只匹配第一个值,多余的数据不会被赋值,也不会报错。
let [a,b,[c]] = [1,2,[3,4]]
console.log(a,b,c)
//1 2 3
//变量结构是一维数组,右侧第三个值是数组,就直接把这个数组整体赋值给 c
let [a,b,c] = [1,2,[3,4]]
console.log(a,b,c)
// 1 2 [3,4]
//变量个数比右侧数据多,多余的变量会得到 `undefined`
let [a,b,c,d] = [1,2,[3,4]]
console.log(a,b,c)
// 1 2 [3,4] undefined
//输出默认值
let [a,b,c,d = 5] = [1,2,[3,4]]
console.log(a,b,c)
// 1 2 [3,4] 5
let [a,b,c,d = 5] = [1,2,[3,4] , 6]
console.log(a,b,c)
// 1 2 [3,4] 6
由此可见解构赋值,结构需要相同。对应的如果没有就是undefined
重命名(取别名)
let user = {
name: 'li',
age: 20
}
//这里重命名(取别名)
let {age:uage,name:uname} = user
console.log(uage,uname)//20 'li'
console.log(age,name)//报错未定义 因为 age、name 从未被声明为变量
对象解构的语法:{ 原属性名: 新变量名 } = 对象
// 1. 普通解构(无重命名):变量名 = 原属性名
let { age, name } = user;
console.log(age, name); // 20 'li' 此时 age、name 是变量
// 2. 重命名解构:变量名 = 新别名,原属性名仅用于匹配
let { age: uage, name: uname } = user;
console.log(uage, uname); // 20 'li'
console.log(age, name); // 报错 原属性名不是变量
重命名 + 默认值
let user = { name: 'li' }; // 缺少 age 属性
// 匹配 user.age(不存在),则 uage 取默认值 18
let { age: uage = 18, name: uname } = user;
console.log(uage, uname); // 18 'li'
字符串解构
let str = 'zhifu'
let [a,b,c,d,e] = str
console.log(a,b,c,d,e)
//z h i f u
字符串解构类似于数组结构,JavaScript 中,字符串是可迭代对象(可以逐个遍历字符),所以字符串在解构时会被自动 “类数组化”—— 把每个字符当作数组的一个元素来匹配。
let str = 'zhifu'
// 解构时,JS 会先把 str 处理成:['z', 'h', 'i', 'f', 'u']
let [a,b,c,d,e] = str;
// 等价于:let [a,b,c,d,e] = ['z', 'h', 'i', 'f', 'u'];
console.log(a,b,c,d,e); // z h i f u
let str = 'zhifu';
let [a,b,c,d,e,f,g] = str;
console.log(a,b,c,d,e,f,g); // z h i f u undefined undefined
let str = 'zhifu';
let [a, , c, , e] = str;
// 跳过第2、4个字符
console.log(a,c,e); // z i u
let str = 'zhi'; // 只有3个字符
let [a,b,c,d = '默认'] = str;
console.log(a,b,c,d); // z h i 默认
let str = 'zhifu';
let [a] = str;
// 只匹配第一个字符
console.log(a); // z
-
简化数据提取, “结构匹配”(变量结构和数组 / 对象结构对应,而非 “数据类型完全相同”—— 变量的嵌套层级要和数据的嵌套层级匹配),匹配时 “能拆就拆,拆不了就整体赋值”,变量数量多于数据数量,或结构不匹配时,未匹配的变量值为
undefined(可通过默认值规避); -
数组解构按位置匹配(通过顺序对应),对象解构按属性名匹配(通过key对应);
-
减少冗余代码,让代码更简洁易读。
默认值的执行规则: “惰性执行” 特性
// 普通赋值:foo() 一定会执行
let a = foo(); // 输出 1
// 解构默认值:foo() 仅在无值时执行
let [b = foo()] = [1]; // 无输出,foo() 没执行
只有在 “变量没有匹配到值” 或 “匹配到的值是 undefined” 时,才会被执行;如果能匹配到有效值(非 undefined),默认值的逻辑完全不会触发
function foo(){
console.log(1)
}
let [a = foo()] = [1] //没有任何输出,因为有值,所以不走默认值逻辑
let [ad = foo()] = [] //输出1 没有值时走默认值
参数解构赋值
function foo ([a,b,c]){
console.log(a,b,c)
}
let arr = [1,2,3]
foo(arr)
//1 2 3
function foo ({name,age}){
console.log(name,age)
}
let arr = {
name: '李'
age: 34
}
foo(arr)
//李 34
返回值结构
function foo() {
let obj= {
name: 'li',
age: 20
}
return obj
}
let {name,age} = foo()
console.log(name,age)
//li 20
模拟后端传回参数:
//json
let jsonData = '{"a":"xxxx","b":"xxxx"}'
//取出对应的值
//字符串转成对象,在将对象解构
let {a,b} = JSON.parse(jsonData)
2.数组方法
for循环
let arr = [1,2,3]
for(let i=0;i<arr.length;i++){
console.log(arr[i])
}
// 1 2 3
break结束循环
let arr = [1,2,3]
for(let i=0;i<arr.length;i++){
// 第1次循环:i=0 → arr[0]=1 → 不满足if条件,执行console.log(1)
// 第2次循环:i=1 → arr[1]=2 → 满足if条件,执行break,直接终止整个循环
if(arr[i]==2){
break // 中断循环,后续代码(包括i++)都不执行
}
console.log(arr[i])
}
// 1
continue跳过当前次
let arr = [1,2,3]
for(let i=0;i<arr.length;i++){
// 第1次循环:i=0 → arr[0]=1 → 不满足if条件,执行console.log(1)
// 第2次循环:i=1 → arr[1]=2 → 满足if条件,执行break,直接终止整个循环
if(arr[i]==2){
continue // // 跳过当前次,不执行后续console.log,直接i++
}
console.log(arr[i])
}
// 输出 1 → 3(跳过了2)
break 会完全终止整个循环(后续迭代都不执行),continue 仅跳过当前次迭代(继续下一次)
遍历间隔元素(比如隔一个遍历)
let arr = [1,2,3,4,5];
for(let i=0; i<arr.length; i+=2){
console.log(arr[i]);
}
// 输出 1 → 3 → 5
forEach循环
遍历数组的每一个元素,并且能方便地获取到元素值、下标,甚至原数组本身
let arr = [1,2,3]
arr.forEach(function(elem,index,array){
// elem:当前遍历到的「元素值」
// index:当前元素的「下标/索引」
// array:遍历的「原数组」
console.log(elem,index)
})
// 1 0 elem:1 元素 index:0 下标
// 2 1
//3 2
在回调里写 return,也只能跳过当前次遍历,无法终止整个循环
let arr = [1,2,3]
arr.forEach(function(elem) {
if (elem === 2) return; // 跳过当前次(不输出2)
console.log(elem); // 输出 1 → 3
});
结合解构赋值
const userArr = [{name: 'li', age: 20}, {name: 'zhang', age: 25}];
userArr.forEach(function({name, age}, index) {
console.log(`第${index}个用户:${name},${age}岁`);
});
// 输出:
// 第0个用户:li,20岁
// 第1个用户:zhang,25岁
| 特性 | for 循环 | forEach 方法 |
|---|---|---|
| 中断遍历 | 可用 break 终止、continue 跳过 | 无法用 break 终止,return 仅跳过当前次 |
| 遍历控制 | 手动控制下标 i,灵活度高 | 自动遍历,无法手动控制下标 |
| 空元素处理 | 可手动判断是否跳过空元素 | 会跳过数组的空元素([1,,3] 只遍历 1 和 3) |
| 异步操作 | 支持(await 可正常生效) | 不支持(await 无法阻塞遍历) |
for 循环的核心优势是可控性强:能用 break 终止遍历、continue 跳过当前次,还能手动控制遍历顺序 / 间隔;
map循环
map的字面意思是 “映射” ——创建新数组,不会修改原数组,而是把原数组每个元素经过处理后,返回成一个新数组
arr.map(function(value){
console.log(value)
})
//1 2 3
let a = arr.map(function(value){
value+=1
return value
})
console.log(arr) //[1,2,3]
console.log(a) //[2,3,4]
| 特性 | map 方法 | forEach 方法 | for 循环 |
|---|---|---|---|
| 返回值 | 返回新数组(长度和原数组一致) | 无返回值(默认返回 undefined) | 无返回值(需手动创建数组) |
| 原数组修改 | 不修改原数组(纯函数特性) | 可手动修改原数组 | 可手动修改原数组 |
| 适用场景 | 数组元素转换(比如数值 + 1、格式转换) | 单纯遍历执行操作(比如打印、修改原数组) | 需中断 / 控制遍历流程的场景 |
filter过滤
filter 遍历原数组的每一个元素,对每个元素执行回调函数,判断回调函数的返回值是否为真,把所有返回 true 的元素收集起来,形成一个新数组
let arr = [1,2,3];
arr.filter(function (value){
console.log(value) // 1 2 3
})
let arr = [1,2,3];
let a = arr.filter(function (value){
return value == 2
})
console.log(arr) //[1,2,3]
console.log(a) //2
let arr = [1,2,3,4,5];
let evenArr = arr.filter(value => value % 2 === 0); // 箭头函数简化
console.log(evenArr); // [2,4]
过滤对象数组
const userArr = [
{name: 'li', age: 20},
{name: 'zhang', age: 25},
{name: 'wang', age: 18}
];
// 过滤出年龄 >=20 的用户
const adultArr = userArr.filter(({age}) => age >= 20);
console.log(adultArr);
// 输出:[{name: 'li', age: 20}, {name: 'zhang', age: 25}]
some
判断数组中是否存在至少一个符合条件的元素,存在则返回 true,不存在则返回 false
let arr = [1,2,3];
arr.some(function (value){
console.log(value)
})
//1 2 3
-
遍历数组元素,依次执行回调函数,判断元素是否符合条件(回调返回布尔值);
-
只要找到第一个符合条件的元素,就立刻终止遍历(性能优化),返回
true; -
如果遍历完所有元素都没有符合条件的,返回
false。let arr = [1,2,3]; let a = arr.some(function (value){ return value==2 }) console.log(a) //true
- 短路遍历(找到符合条件的元素就停止) ,区别于
filter/map(会遍历所有元素)。 - 空数组调用
some直接返回false
let arr = [1,2,3]; let a = arr.some(value => value==2);
// 一行搞定,效果一致
console.log(a); // true
//判断用户列表中是否有成年人(年龄≥18)
let users = [{name: 'li', age: 17}, {name: 'zhang', age: 20}];
let hasAdult = users.some(user => user.age >= 18);
console.log(hasAdult); // true
| 方法 | 核心作用 | 返回值 | 遍历特性 |
|---|---|---|---|
some | 判断是否至少一个元素符合条件 | 布尔值(true/false) | 短路遍历(找到即停止) |
every | 判断是否所有元素符合条件 | 布尔值(true/false) | 短路遍历(找到不符合即停止) |
filter | 过滤出所有符合条件的元素 | 新数组(符合条件的元素) | 遍历所有元素 |
every检查每一个元素是否都符合调试,每一个都符合才返回true,some一个符合就返回true
reduce()
reduce的字面意思是 “归约 / 汇总”,核心作用是:遍历数组,将数组元素通过回调函数 “累积计算” 成一个最终值(可以是数字、对象、数组等)
let arr = [1,2,3]
//prev 上一次回调的返回值(“累积值”)
//cur 当前遍历的元素
//index 当前元素的下标
//array 原数组
let a = arr.reduce(function(prev,cur,index,array){
return prev+cur
},0)// 初始值为 0
console.log(a)
//6
每一次回调的返回值,都会成为下一次回调的 prev,直到遍历结束,最终返回最后一次的 prev,如果省略第二个参数(初始值),reduce 会把数组第一个元素作为初始的 prev,从第二个元素开始遍历.
| 遍历次数 | prev(累积值) | cur(当前元素) | index | 计算逻辑 | 返回值(新的 prev) |
|---|---|---|---|---|---|
| 第 1 次 | 0(初始值) | 1 | 0 | 0 + 1 | 1 |
| 第 2 次 | 1(上一次返回值) | 2 | 1 | 1 + 2 | 3 |
| 第 3 次 | 3(上一次返回值) | 3 | 2 | 3 + 3 | 6 |
| 遍历结束 | - | - | - | - | 最终返回 6 |
空数组调用 reduce 且无初始值会报错
let emptyArr = [];
// 报错:Reduce of empty array with no initial value
emptyArr.reduce(() => {});
emptyArr.reduce(() => {}, 0); // 不报错
扁平化二维数组(转一维)
let arr = [[1,2], [3,4], [5,6]];
let flatArr = arr.reduce((prev, cur) => prev.concat(cur), []); // 初始值为空数组
console.log(flatArr); // [1,2,3,4,5,6]
求最大值
let arr = [1,2,5,4,0]
let a = arr.reduce(function(prev,cur){
return Math.max(prev,cur)
})
console.log(a) //5
去重
let arr = [1,2,2,4,0,4,3]
let a = arr.reduce(function(prev,cur){
// prev.indexOf(cur) == -1 → 判断当前元素是否不在去重数组中
// && 短路逻辑 → 只有前面为 true 时,才执行 prev.push(cur)
prev.indexOf(cur) == -1 && prev.push(cur)
return prev // 每次返回更新后的去重数组
},[])// 初始值为空数组(prev 一开始是 [])
console.log(a) //1 2 4 0 3
| 遍历次数 | prev(去重数组) | cur(当前元素) | indexOf 判断 | 操作 | 新的 prev |
|---|---|---|---|---|---|
| 第 1 次 | [] | 1 | [].indexOf(1) → -1 | push(1) | [1] |
| 第 2 次 | [1] | 2 | [1].indexOf(2) → -1 | push(2) | [1,2] |
| 第 3 次 | [1,2] | 2 | [1,2].indexOf(2) → 1 | 不操作 | [1,2] |
| 第 4 次 | [1,2] | 4 | [1,2].indexOf(4) → -1 | push(4) | [1,2,4] |
| 第 5 次 | [1,2,4] | 0 | [1,2,4].indexOf(0) → -1 | push(0) | [1,2,4,0] |
| 第 6 次 | [1,2,4,0] | 4 | [1,2,4,0].indexOf(4) → 3 | 不操作 | [1,2,4,0] |
| 第 7 次 | [1,2,4,0] | 3 | [1,2,4,0].indexOf(3) → -1 | push(3) | [1,2,4,0,3] |
| 遍历结束 | - | - | - | - | 最终返回 [1,2,4,0,3] |
用 includes 替代 indexOf
let arr = [1,2,2,4,0,4,3];
let a = arr.reduce((prev, cur) => {
// 用 includes 判断元素是否存在
if (!prev.includes(cur)) {
prev.push(cur);
}
return prev;
}, []);
console.log(a); // [1,2,4,0,3]
reduce 实现 “对象数组去重”
// 需求:根据 id 去重用户数组
let userArr = [
{id: 1, name: 'li'},
{id: 2, name: 'zhang'},
{id: 1, name: 'li'}, // 重复 id=1
{id: 3, name: 'wang'}
];
let uniqueUsers = userArr.reduce((prev, cur) => {
// 判断 prev 中是否已有相同 id 的用户
const isExist = prev.some(user => user.id === cur.id);
if (!isExist) {
prev.push(cur);
}
return prev;
}, []);
console.log(uniqueUsers);
// 输出:[{id:1,name:'li'}, {id:2,name:'zhang'}, {id:3,name:'wang'}]
for in
for in 它会遍历到数组原型链上的自定义方法 / 属性,for...in 本质是遍历对象的可枚举属性,数组也是特殊的对象(下标就是属性名)
for(let index in arr){
console.log(arr[index])
//1 2 3
}
let arr = [1,2,3];
// 给数组原型添加自定义方法(可枚举)
Array.prototype.myMethod = function() {};
// 用 for...in 遍历
for(let index in arr) {
console.log(index + ': ' + arr[index]);
}
// 输出:
// 0: 1
// 1: 2
// 2: 3
// myMethod: function() {} 原型链的方法被遍历到了
如果非要用 for...in 遍历数组,必须用 hasOwnProperty 判断属性是否是数组 “自身的”:
let arr = [1,2,3];
Array.prototype.myMethod = function() {};
for(let index in arr) {
// 只处理数组自身的属性,跳过原型链属性
if (arr.hasOwnProperty(index)) {
console.log(arr[index]); // 仅输出 1、2、3
}
}
find
查找数组中第一个符合条件的元素,找到第一个使回调返回 true 的元素,立即终止遍历(性能优化),并返回该元素;找不到则返回 undefined
let arr = [1,2,3,4,3,1,0]
let a = arr.find(function(value){
return value == 2
})
console.log(a) //2
const users = [
{id: 1, name: 'li'},
{id: 2, name: 'zhang'},
{id: 3, name: 'wang'}
];
// 查找 id=2 的用户
const targetUser = users.find(user => user.id === 2);
console.log(targetUser); // {id: 2, name: 'zhang'}
findIndex 是第一次元素出现的下标,从0开始.如果找不到,就返回 -1
let arr = [1,2,3,4,3,1,0];
// 查找第一个值为 2 的元素的下标
let a = arr.findIndex(function(value){
return value == 2;
});
console.log(a); // 1(2 出现在下标 1 的位置)
for of
直接遍历数组的元素值,遍历数组时,item 直接等于当前元素的值.只遍历数组自身的元素,不会遍历原型链上的属性 / 方法.
let arr = [1,2,3]
for(let item of arr){
console.log(item)
}
// 1 2 3
可直接中断遍历(支持 break/continue)
let arr = [1,2,3,4];
for(let item of arr){
if(item === 2) break; // 找到2后终止遍历
console.log(item); // 仅输出 1
}
获取下标和元素:搭配Array.entries()
let arr = [1,2,3]
for(let [index,item] of arr.entries()){
console.log(index,item)
}
// 0 1
// 1 2
// 2 3
获取下标: Array.keys()
let arr = [1,2,3]
for(let item of arr.keys()){
console.log(item)
}
//0 1 2
支持遍历其他可迭代对象(字符串 / Set/Map)
// 遍历字符串
for(let char of 'zhifu'){
console.log(char); // 输出 z、h、i、f、u
}
// 遍历 Set(自动去重)
let s = new Set([1,2,2,3]);
for(let item of s){
console.log(item); // 输出 1、2、3
}
判断变量是否为数组
Array.isArray() 会直接检测变量的内部,判断其是否为数组类型
let arr = [1,2,3]
console.log(Array.isArray(arr)); // true(数组)
console.log(Array.isArray([])); // true(空数组)
Object.prototype.toString.call不受原型链、自定义属性的干扰,是最精准的类型判断方式。
function isArray(variable) {
return Object.prototype.toString.call(variable) === '[object Array]';
}
// 测试
console.log(isArray([1,2,3])); // true
console.log(isArray([])); // true
console.log(isArray(new Array())); // true(构造函数创建的数组)
console.log(Object.prototype.toString.call(123)); // [object Number] console.log(Object.prototype.toString.call('abc')); // [object String] console.log(Object.prototype.toString.call(true)); // [object Boolean]
instanceof
let arr = [1,2,3];
console.log(arr instanceof Array); // true
console.log({} instanceof Array); // false
原型链被修改时失效:如果手动修改数组的原型,instanceof 会出错
let arr = [1,2,3];
arr.__proto__ = Object.prototype; // 修改原型链
console.log(arr instanceof Array); // false(误判)
typeof 对数组的判断结果是 object,无法区分数组和普通对象