javaScript理解四

5 阅读14分钟

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]; // 交换ab 
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])
    }
    
   // 输出 13(跳过了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
       
    
  1. 短路遍历(找到符合条件的元素就停止) ,区别于 filter/map(会遍历所有元素)。
  2. 空数组调用 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(初始值)100 + 11
第 2 次1(上一次返回值)211 + 23
第 3 次3(上一次返回值)323 + 36
遍历结束----最终返回 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) → -1push(1)[1]
第 2 次[1]2[1].indexOf(2) → -1push(2)[1,2]
第 3 次[1,2]2[1,2].indexOf(2) → 1不操作[1,2]
第 4 次[1,2]4[1,2].indexOf(4) → -1push(4)[1,2,4]
第 5 次[1,2,4]0[1,2,4].indexOf(0) → -1push(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) → -1push(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,无法区分数组和普通对象