美文网首页js css html
js常用的循环遍历及数组常用方法

js常用的循环遍历及数组常用方法

作者: 没名字的某某人 | 来源:发表于2021-06-01 14:28 被阅读0次

    写在最前:文章转自掘金

    数组遍历

    随着 JS 的不断发展,截至 ES7 规范已经有十多种遍历方法。下面按照功能类似的方法为一组,来介绍数组的常用遍历方法。

    for、forEach、for ...of

    const list = [1, 2, 3, 4, 5, 6, 7, 8,, 10, 11];
    
    for (let i = 0, len = list.length; i < len; i++) {
      if (list[i] === 5) {
        break; // 1 2 3 4
        // continue; // 1 2 3 4 6 7 8 undefined 10 11
      }
      console.log(list[i]);
    }
    
    for (const item of list) {
      if (item === 5) {
        break; // 1 2 3 4
        // continue; // 1 2 3 4 6 7 8 undefined 10 11
      }
      console.log(item);
    }
    
    list.forEach((item, index, arr) => {
      if (item === 5) return;
      console.log(index); // 0 1 2 3 5 6 7 9 10
      console.log(item); // 1 2 3 4 6 7 8  10 11
    });
    

    小结

    • 三者都是基本的由左到右遍历数组
    • forEach 无法跳出循环;forfor ..of 可以使用 break 或者 continue 跳过或中断。
    • for ...of 直接访问的是实际元素。for 遍历数组索引,forEach 回调函数参数更丰富,元素、索引、原数组都可以获取。
    • for ...offor 如果数组中存在空元素,同样会执行。

    补充 forEach中执行异步任务
    forEach中执行异步任务,不会等待异步任务完成,这个时候可以用 for ..of替代。

    some、every

    const ages = [3, 10, 18, 20];
    const someAdult = ages.some(item => item>=18);
    // someAdult: true
    const everyAdult = ages.every(item => item>=18);
    // someAdult: false
    

    小结

    • 二者都是用来做数组条件判断的,都是返回一个布尔值
    • 二者都可以被中断
    • some 若某一元素满足条件,返回 true,循环中断;所有元素不满足条件,返回 false
    • everysome 相反,若有一元素不满足条件,返回 false,循环中断;所有元素满足条件,返回 true

    filter、map

    const list = [
    { name: '头部导航', type: 'nav', id: 1 },,
    { name: '轮播', type: 'content', id: 2 },
    { name: '页脚', type: 'nav', id: 3 },
    ];
    const resultList = list.filter(item => {
      console.log(item);
      return item.type === 'nav';
    });
    // resultList: [
    //   { name: '头部导航', type: 'nav', id: 1 },
    //   { name: '页脚', type: 'nav', id: 3 },
    // ]
    
    const newList = list.map(item => {
      console.log(item);
      return item.id;
    });
    // newList: [1, empty, 2, 3]
    
    // list: [
    //   { name: '头部导航', type: 'nav', id: 1 },
    //   empty,
    //   { name: '轮播', type: 'content', id: 2 },
    //   { name: '页脚', type: 'nav', id: 3 },
    // ]
    

    小结

    • 其实map还类似forEach,只不过map返回新数组,不跳过空元素。
    • 二者都是生成一个新数组,都不会改变原数组(不包括遍历对象数组是,在回调函数中操作元素对象)
    • 二者都会跳过空元素。有兴趣的同学可以自己打印一下
    • map 会将回调函数的返回值组成一个新数组,数组长度与原数组一致。
    • filter 会将符合回调函数条件的元素组成一个新数组,数组长度与原数组不同。
    • map 生成的新数组元素是可自定义。
    • filter 生成的新数组元素不可自定义,与对应原数组元素一致。

    find、findIndex

    const list = [
    { name: '头部导航', id: 1 },
    { name: '轮播', id: 2 },
    { name: '页脚', id: 3 },
    ];
    const result = list.find((item) => item.id === 3);
    // result: { name: '页脚', id: 3 }
    result.name = '底部导航';
    // list: [
    //   { name: '头部导航', id: 1 },
    //   { name: '轮播', id: 2 },
    //   { name: '底部导航', id: 3 },
    // ]
    
    const index = list.findIndex((item) => item.id === 3);
    // index: 2
    list[index].name // '底部导航';
    

    小结

    • 二者都是用来查找数组元素。
    • find 方法返回数组中满足 callback 函数的第一个元素的值。如果不存在返回undefined
    • findIndex 它返回数组中找到的元素的索引,而不是其值,如果不存在返回 -1。

    reduce、reduceRight

    reduce 接收 2 个参数: 第一个参数是回调函数(必选),第二个参数是初始值 initialValue(可选)。
    而第一个参数(回调函数),接收下面四个参数:

    • Accumulator (acc) (累计器)
    • Current Value (cur) (当前值)
    • Current Index (idx) (当前索引)
    • Source Array (src) (源数组)

    reduceRight 方法除了与reduce执行方向相反外(从右往左),其他完全与其一致。
    我们平时在业务中多数使用到的是前两个参数,并且有以下两种情况。

    不带初始值

    [6,2,3,4].reduce((acc, cur) => {
      return acc + cur
    })
    // 6 + 2 + 3 + 4
    // 15
    

    带初始值

    [6,2,3,4].reduce((acc, cur) => {
      return acc + cur
    }, 10)
    // 10 + 6 + 2 + 3 + 4
    // 25
    

    注意,初始值 initialValue 可以是任意类型。如果没有提供 initialValuereduce 会从索引 1 的地方开始执行 callback 方法,第一次的acc的值是数组的第一个值。如果提供 initialValue,从索引 0 开始。

    计算对象数组某一属性的总和

    const list  = [
      { name: 'left', width: 20 },
      { name: 'center', width: 70 },
      { name: 'right', width: 10 },
    ];
    list.reduce((acc,cur)=>{
      return acc+cur.width
    },0)
    

    对象数组的去重,并统计每一项重复次数

    const list  = [
      { name: 'left', width: 20 },
      { name: 'right', width: 10 },
      { name: 'center', width: 70 },
      { name: 'right', width: 10 },
      { name: 'left', width: 20 },
      { name: 'right', width: 10 },
    ];
    const repeatTime = {};
    const result = list.reduce((acc,cur)=>{
      if(repeatTime[cur.name]){  // 从重复集合中判断是否重复
        repeatTime[cur.name]++  // 累加重复集合中的次数
        return acc   // 返回累加器
       }
       repeatTime[cur.name] = 1   // 元素加入重复集合并将次数初始化为1
       return [...acc, cur]        // 返回累加器并加入非重复新元素
    },[])   //  设置累加器初始值为空数组,用于累加不重复元素
    

    对象数组最大/最小值获取

    const list  = [
      { name: 'left', width: 20 },
      { name: 'right', width: 30 },
      { name: 'center', width: 70 },
      { name: 'top', width: 40 },
      { name: 'bottom', width: 20 },
    ];
    const max = list.reduce((curItem, item) => {
      return curItem.width >= item.width ? curItem : item;
    });
    const min = list.reduce((curItem, item) => {
      return curItem.width <= item.width ? curItem : item;
    });
    // max: { name: "center", width: 70 }
    // min: { name: "left", width: 20 }
    

    reduce 很强大,更多奇技淫巧推荐查看这篇《25个你不得不知道的数组reduce高级用法》

    性能对比

    我们在 Chrome 浏览器中尝试。我采用每个循环执行10次,去除最大、最小值 取平均数,降低误差。

    是否可终止
    **** break continue return 性能(ms)
    for 终止 ✅ 跳出本次循环 ✅ 2.42
    forEach 3.12
    map 3.74
    for of 终止 ✅ 跳出本次循环 ✅ 6.33
    some return true ✅ 2.78
    every return false✅ 2.75

    注意,不同浏览器内核也会有些差异。

    对象遍历

    在对象遍历中,经常需要遍历对象的键、值,ES5 提供了 for...in 用来遍历对象,然而其涉及对象属性的“可枚举属性”、原型链属性等,下面将从 Object 对象本质探寻各种遍历对象的方法,并区分常用方法的一些特点。

    for in

    Object.prototype.fun = () => {};
    const obj = { 2: 'a', 1: 'b' };
    for (const i in obj) {  console.log(i, ':', obj[i]);}
    // 1: b// 2: a// fun : () => {} 
    // Object 原型链上扩展的方法也被遍历出来
    // for (const i in obj) {  if (Object.prototype.hasOwnProperty.call(obj, i)) { console.log(i, ':', obj[i]); }}
    // name : a 不属于自身的属性将被 hasOwnProperty 过滤
    

    小结
    使用 for in 循环时,返回的是所有能够通过对象访问的、可枚举的属性,既包括存在于实例中的属性,也包括存在于原型中的实例。如果只需要获取对象的实例属性,可以使用 hasOwnProperty 进行过滤。

    注意,要使用(const x in a)而不是(x in a),后者将会创建一个全局变量。

    for in 的循环顺序,参考【JavaScript 权威指南】(第七版)6.6.1。

    • 先列出名字为非负整数的字符串属性,按照数值顺序从最小到最大。这条规则意味着数组和类数组对象的属性会按照顺序被枚举。
    • 在列出类数组索引的所有属性之后,在列出所有剩下的字符串名字(包括看起来像整负数或浮点数的名字)的属性。这些属性按照它们添加到对象的先后顺序列出。对于在对象字面量中定义的属性,按照他们在字面量中出现的顺序列出。
    • 最后,名字为符号对象的属性按照它们添加到对象的先后顺序列出。

    Object.keys

    Object.prototype.fun = () => {};
    const str = 'ab';
    console.log(Object.keys(str));
    // ['0', '1']
    const arr = ['a', 'b'];
    console.log(Object.keys(arr));
    // ['0', '1']
    const obj = { 3: 'b', 2: 'a' };
    console.log(Object.keys(obj));
    // ['2', '3']
    

    小结
    用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组。

    Object.values

    Object.prototype.fun = () => {};
    const str = 'ab';
    console.log(Object.values(str));
    // ['a', 'b']
    const arr = ['a', 'b'];
    console.log(Object.values(arr));
    // ['a', 'b']
    const obj = { 1: 'b', 0: 'a' };
    console.log(Object.values(obj));
    // ['a', 'b']
    

    小结
    用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性值组成的数组。

    Object.entries

    const str = 'ab';
    for (const [key, value] of Object.entries(str)) {  console.log(`${key}: ${value}`);}
    // 0: a// 1: b
    const arr = ['a', 'b'];
    for (const [key, value] of Object.entries(arr)) {  console.log(`${key}: ${value}`);}
    // 0: a// 1: b
    const obj = { 1: 'b', 0: 'a' };
    for (const [key, value] of Object.entries(obj)) {  console.log(`${key}: ${value}`);}
    // 0: a// 1: b
    

    小结
    用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回二维数组。每一个子数组由对象的属性名、属性值组成。可以同时拿到属性名与属性值的方法。

    Object.getOwnPropertyNames

    Object.prototype.fun = () => {};
    Array.prototype.fun = () => {};
    const str = 'ab';
    console.log(Object.getOwnPropertyNames(str));
    // ['0', '1', 'length']
    const arr = ['a', 'b'];
    console.log(Object.getOwnPropertyNames(arr));
    // ['0', '1', 'length']
    const obj = { 1: 'b', 0: 'a' };
    console.log(Object.getOwnPropertyNames(obj));
    // ['0', '1']
    

    小结
    用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组

    数组常用方法

    indexOf

    用于查找数组中是否存在某个值,如果存在则返回某个值的下标,否则返回-1

    let list = [1, 2, 3];
    console.log(list.indexOf(2))  // 1
    console.log(list.indexOf('饼干'))  // -1
    

    splice

    用于数组删除或替换内容,接收三个参数,无返回值

    • 第一个参数,删除或添加的位置
    • 第二个参数,要删除几位,如果为0则不删除
    • 第三个参数,向数组添加内容
    let list = [1, 2, 3];
    
    list.splice(0, 1)  // 从下标0开始删除一位
    console.log(list) // [2, 3]
    
    list.splice(0, 1, '饼干') // 从下标0开始删除一位,添加一个字符串
    console.log(list) // ['饼干', 2, 3]
    
    list.splice(0, 0, '饼干') // 从下标0开始删除0位,添加一个字符串
    console.log(list) // ['饼干', 1, 2, 3]
    

    slice

    用于截取数组值,接收两个参数,返回截取后的数组

    • 第一个参数,开始截取的下标,如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素
    • 第二个参数,结束截取的下标的前一位,负数同上
    let list = [1, 2, 3, 4, 5]
    let res = list.slice(1, 4)  //从下标1开始,截取到下标为4的前一位
    console.log(res)  // [2, 3, 4]
    

    reverse

    用于数组反转,返回新数组

    let list = [1, 2, 3]
    let res = list.reverse()
    console.log(res) //  [3, 2, 1]
    

    join

    用于数组以什么形式拼接,返回字符串

    let list = [1, 2, 3]
    let res = list.join('-')
    console.log(res)  // 1-2-3
    

    sort

    用于数组排序,接收函数参数,排序规则根据参数函数返回值,返回新数组

    • 返回值为正数,后面的挪到前面
    • 返回值为负数,保持不动
    • 返回值为0,保持不动
    let list = [1, 2, 3]
    let sortRes = list.sort((a, b)=> b-a)
    console.log(sortRes)  // [3, 2, 1]
    

    concat

    用于合并数组,返回新数组

    let list = [1, 2, 3]
    let res = list.concat([4, 5, 6])
    console.log(res)  // [1, 2, 3, 4, 5, 6]
    

    push

    向数组后面添加元素,返回值为数组的length

    let list = [1, 2, 3];
    let res = list.push(1)
    console.log(res) // 4
    

    unshift

    向数组的头部添加元素,返回值为数组的length

    pop

    用于删除数组尾部的元素,返回值为删除的元素

    shift

    用于删除数组的头部,返回值为删除的元素

    toString

    用于将数组内容转换为字符串,返回字符串

    Array.isArray()

    判断接收的参数是不是一个数组,返回布尔

    includes

    检测数组中是否存在该元素,返回布尔

    find

    查找数组的元素,接收函数为参数,满足条件返回单个数组元素,按照就近原则返回,没找到返回undefind

    let list = [1, '饼干', 3];
    let res = list.find(item => item == '饼干')
    console.log(res) // 饼干
    

    findIndex

    查找数组中元素,接收函数为参数,满足条件的返回数组下标,没找到返回-1

    let list = [1, 2, 3];
    let res = list.findIndex((item) => item > 1)
    console.log(res) // 1, 按照就近原则返回下标
    

    flat

    用于拉平嵌套数组,接收一个整数,表示想要拉平的层数,默认为1,如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字,返回新数组

    let list = [1, 2, 3, [4, [5]]];
    let res = list.flat(Infinity)
    console.log(res) // [1, 2, 3, 4, 5]
    

    fill

    使用固定值填充数组

    let list = [1, 2, 3];
    let res = list.fill(1)
    console.log(res) // [1, 1, 1]
    

    Array.from

    将类数组转换为数组类型,例如arguments,返回新数组

    let res = Array.from(document.getElementsByTagName("div"))
    console.log(res) // 转换为真数组就可以调用数组原型的方法
    

    Array.of

    用于生成一个数组对象,主要是用来弥补Array()的不足

    let res = Array.of(1, 2, 3)
    console.log(res) // [1, 2, 3]
    

    小结
    改变原始数组值的有哪些
    forEachsplicereversesortpushpopshiftunshiftfill

    相关文章

      网友评论

        本文标题:js常用的循环遍历及数组常用方法

        本文链接:https://www.haomeiwen.com/subject/yllbsltx.html