js数组的方法

637 阅读8分钟

js数组的方法

1变异方法

1.push(), pop(), shift()从数组的前面删除一项, unshift()从数组的前面增加一项.原数组会改变,返回值为操作后新数组的length

   let arr = [1, 2, 3, 4];
   //push
   let push_arr = arr.push('hellow');
   console.log(arr); // [1, 2, 3, 4, 'hellow'];
   console.log(push_arr); // 5

2.sort() 排序,原数组会改变, 返回值为操作后新数组

 const persons = [{
               name: 'bwf',
               age: 30,
               priority: 2
           }, {
               name: 'wmy',
               age: 31,
               priority: 3
           }, {
               name: 'wxy',
               age: 2,
               priority: 1

           }, {
               name: 'wxyy',
               age: 0.5,
               priority: 1
           }, ]
           // p1-p2,升序,从小->大排序
           // persons.sort((p1, p2) => p1.age - p2.age)
           // p2-p1,降序,从大->小排序
       persons.sort((p1, p2) => p2.age - p1.age)
       console.log(persons)
           // 需求:优先级升序排列,年龄降序排列(双重排序
           // 优先级升序yanyan yueyue bwf wmy       年龄降序yueyue   yanyan bwf wmy
       persons.sort((p1, p2) => {
           const priority1 = p1.priority;
           const priority2 = p2.priority;
           if (priority1 === priority2) {
               p2.age - p1.age
           }
           return priority1 - priority2;
       })

3. reverse() 反转数组, 原数组会改变, 返回值为操作后新数组

   const arr = [1, 2, 3, 4, 5];
   const reverseArr = arr.reverse;
   console.log(arr, reverseArr)

4. splice() 增删改,原数组会改变,返回值为操作项组成的数组

  • arr.splice(index , howmany , item1,.....,itemX)
  • index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
  • howmany:必需。要删除的项目数量。值是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
  • item1, ..., itemX:可选。向数组添加的新项目。
4.1 删除
   let arr = ['哈哈', '呜呜', '嘿嘿', '啦啦']
    let newArr = arr.splice(1, 1)
    console.log(newArr) // ['呜呜']
    console.log(arr) // ['哈哈', '嘿嘿', '啦啦']
4.2 替换
let arr = ['哈哈', '呜呜', '嘿嘿', '啦啦']
let newArr = arr.splice(1, 1, 'new')
console.log(newArr) // ["呜呜"]
console.log(arr) // ["哈哈", "new", "嘿嘿", "啦啦"]
4.3 增加
let arr = ['哈哈', '呜呜', '嘿嘿', '啦啦']
let newArr = arr.splice(1, 0, 'new')
console.log(newArr) // []
console.log(arr) // ["哈哈", "new", '呜呜', "嘿嘿", "啦啦"]

需求:面包屑导航(点击按钮增加一个导航栏,超过5级,则第二级显示为省略号,hover省略号可查看)

2非变异方法(原数组一般情况下不会改变)

1.map方法

1.1 map() 遍历/映射 原数组不会改变,返回值是return组成的新数组,没有return,那就映射成undefined返回、
 let arr = [{
        age: 1,
        name: '张三'
    }, {
        age: 2,
        name: '李四'
    }]
    let newArr = arr.map(x => {
        if (x.age === 2) {
            return x.name
        }
    })
    console.log(newArr) //[undefined, '李四']
1.2 map当然也可以直接处理原数组,这个时候作用类似forEach()
 //这个情况下不用新数组去接收了
    let arr = [{
        age: 1,
        name: '张三'
    }, {
        age: 2,
        name: '李四'
    }, {
        age: 3,
        name: '王五'
    }]
    let newArr = arr.map(x => {
        if (x.age === 2) {
            x.name = "xxxxxx"
        }
    })
     console.log(arr) //[{ age: 1, name: '张三' }, { age: 2, name: 'xxxxxx' }, { age: 3, name: '王五' }]
     console.log(newArr) //[undefined, undefined, undefined]
     
     //forEach实现上面的需求
     arr.forEach(item => {
        if (item.age === 2) {
            item.name = "xxxxxx"
         }
    })
1.3 map 和 forEach的弊端是无法使用break, continue等关键字,只能使用return,作用类似原生for循环的continue,意味着无法终止循环。要想找到某一条件项后终止整个循环还是得用原生for配合break遍历。
  // return后只是该项不会进行下面打印操作,但是数组还是接着遍历下一项
     let arr = [{
         age: 1,
         name: '张三'
     }, {
         age: 2,
         name: '李四'
     }, {
         age: 3,
         name: '王五'
     }]
     let newArr = arr.map(x => {
         if (x.age === 2) {
             return;
         }
         console.log(x.name)
     }) //张三  王五

2. forEach() 遍历 返回值为undefined,常用于遍历操作原数组

 let arr = [{
     age: 1,
     name: '张三'
 }, {
     age: 2,
     name: '李四'
 }]
 let newArr = arr.forEach(x => {
     x.sex = '男'
 })
 console.log(newArr) // undefined
 console.log(arr) // [{age:1, name:'张三', sex:'男'},{age:2, name:'李四', sex:'男'}]

3. join() (数组转字符串)常和split() 配合实现字符串的部分替换,返回值为字符串,原数组不变,默认为逗号分隔符

let arr = [1, 2, 3, 4];
console.log(arr.join()); // 1,2,3,4

4. concat() 连接多个数组,返回值为连接后的数组,原数组不变

  let arr1 = [1, 2, 3, 4];
  let arr2 = [5]
  let arr3 = [6]
  console.log(arr1.concat(arr2, arr3)) // [1, 2, 3, 4, 5, 6]

5. slice()截取。原数组不变,返回值为截取的数组

  • arr.slice(startIndex , endIndex);

  • startIndex为负数时,表示从尾部开始截取

  • endIndex 可选参数,为结束的索引,注意不包含此项,则表示截取到最后一项

  • 当 startIndex , endIndex都没提供时,表示截取整个数组,此时是拷贝数组常用的一种方法。注意当数组里面的值是引用类型时,该拷贝也是浅拷贝。

    let arr = [1, 2, 3, 4]
    console.log(arr.slice(1, 2)) // [2]
    let arr1 = [1, 2, 3, 4]
    let arr2 = arr1.slice()
    console.log(arr2) // [1, 2, 3, 4]
    arr1[0] = 99
    console.log(arr2) //  [1, 2, 3, 4]   数组的值为基本类型时是深拷贝
    
    
    
    let arr3 = [{
        name: "bwf"
    }, {
        name: "wmy"
    }, ];
    let arr4 = arr3.slice();
    arr3[0].name = "xxxx";
    console.log(arr4) //[{ name: "xxxx" }, { name: "wmy" },]数组的值为引用类型时是浅拷贝

6. indexOf()和 lastIndexOf() 返回检测到该项的索引,数组中若没有该项,则返回-1,常用于检查一维数组中是否含有某项,无法直接检查[{name: 'zs'}, {name:'ls'}]这样的数组

  let arr = [1, 2, 3, 4]
   console.log(arr.indexOf(2)) // 1
   console.log(arr.indexOf(5)) // -1

7. filter() 过滤,返回值: 值为true的组装到一个新数组。原数组不变。

  let arr = [1, 2, 3, 4];
   let result = arr.filter((item) => {
       return item > 3;
   });
   console.log(result) // [4]

8. some()、every() 检测符合条件的,返回值为布尔值,原数组不变。

 // some() return中有一项为true, 结果为true,而且后面项不再进行遍历; every() return所有项为true,结果才为true。若有一项为false,立即返回false.后面的项不再进行遍历。

let arr = [1, 2, 3, 4];
let resultSome = arr.some((item) => {
   return item > 3;
});
let resultEvery = arr.every((item) => {
   return item > 3;
});
console.log(resultSome) // true
console.log(resultEvery) // false

9. find() findIndex ()查找,原数组不变。

   // find()返回符合条件的项,没有符合条件的项则返回 undefined。匹配到了后面的就不再遍历了。
   // findIndex ()返回符合项的索引,没有符合的则返回-1

   let arr = [1, 2, 3, 4];
   let resultFind = arr.find((item) => {
       return item > 2;
   });
   console.log(resultFind) // 3

10. Array.from()将伪数组(有length属性但没有数组方法的对象)转化成真正的数组,常配合new Set() 等使用。原数组不变,返回值为新数组。

 let obj = {
    'name': '哈哈',
    'age': '13',
    '0': '张三',
    length: 3
}
let arr = Array.from(obj);
console.log(arr) // ["张三", undefined, undefined]

10. reduce() 数组的归并方法,除遍历外可同时将前面数组项遍历产生的结果与当前遍历项进行运算

  • arr.reduce(function(prev,cur,index,arr){ }, init);
  • prev: 表示上一次调用回调时的返回值
  • 回调函数第一次执行时,prev 和cur的取值有两种情况:
  1. 如果调用reduce()时提供了init,prev取值为init,cur取数组中的第一个值;
  2. 如果没有提供 init,那么prev取数组中的第一个值,cur取数组中的第二个值。
  • cur: 表示当前正在处理的数组元素;
  • index: 表示当前正在处理的数组元素的索引;
  • init: 表示初始值可以为空数组 空对象等。如果没有提供初始值,则将使用数组中的第一个元素。

** 注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。提供初始值通常更安全。**

 //求和
  let arr = [3, 9, 4, 3, 6, 0, 9];
  let sum = arr.reduce((prev, cur, index) => prev + cur, 0)
  console.log(sum);

  //求最大值
  let max = arr.reduce((prev, cur, index) =>
      Math.max(prev, cur)
  )
  console.log(max);


  // reduce() 没有初始值.
  var maxCallback = (acc, cur) => Math.max(acc.x, cur.x)
  var maxCallback2 = (max, cur) => Math.max(max, cur);
  const res1 = [{
      x: 2
  }, {
      x: 22
  }, {
      x: 42
  }].reduce(maxCallback);
  // 第一次执行结果为22 第二次执行Math.max(22.x, 42) //undefined
  console.log('res1', res1); //NaN


  // map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
  const res2 = [{
          x: 22
      }, {
          x: 42
      }].map(el => el.x)
      .reduce(maxCallback2, -Infinity);
  console.log('res2', res2)




  //去重
  let newArr = arr.reduce((prev, cur, index) => {
      prev.indexOf(cur) === -1 && prev.push(cur)
      return prev
  }, [])
  console.log(newArr);



  // 将二维数组转化为一维
  var flattened = [
      [0, 1],
      [2, 3],
      [4, 5]
  ].reduce((a, b) => a.concat(b), []

  );
  console.log('flattened', flattened)
  // flattened is [0, 1, 2, 3, 4, 5]


  // 计算数组中每个元素出现的次数 这个真的好方便啊
  var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

  var countedNames = names.reduce(function(allNames, name) {
      if (name in allNames) {
          allNames[name]++;
      } else {
          allNames[name] = 1;
      }
      return allNames;
  }, {});
  // countedNames is:
  // { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

  // 重组
  let arr2 = [{
      name: "王1",
      age: 16
  }, {
      name: "李2",
      age: 20
  }, {
      name: "张3",
      age: 30
  }, ]
  let arr3 = ["userWang", "userLi", "userZhang"]
  let userObj = arr2.reduce((prev, cur, index) => {
      prev[arr3[index]] = cur;
      return prev;
  }, {})
  console.log(userObj); // {userLi: {name: "李2", age: 20}, userWang: { name: "王1", age: 16 }, userZhang: { name: "张3", age: 30 } }


  // 按属性对object分类
  var people = [{
      name: 'Alice',
      age: 21
  }, {
      name: 'Max',
      age: 20
  }, {
      name: 'Jane',
      age: 20
  }];

  function groupBy(objectArray, property) {
      return objectArray.reduce(function(acc, obj) {
          var key = obj[property];
          if (!acc[key]) {
              acc[key] = [];
          }
          acc[key].push(obj);
          return acc;
      }, {});
  }

  var groupedPeople = groupBy(people, 'age');
  // groupedPeople is:
  // {
  //   20: [
  //     { name: 'Max', age: 20 },
  //     { name: 'Jane', age: 20 }
  //   ],
  //   21: [{ name: 'Alice', age: 21 }]
  // }

11. for...of, for...in(for...of 是es6新增的)

  • for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
    • 原生具备 Iterator 接口的数据结构如下: Array ,Map,Set,String,TypedArray,函数的 arguments 对象,NodeList 对象
  • for...in 既可以直接遍历普通对象,又可以遍历数组;而for...of只能直接遍历的是部署了 Iterator 接口结构。对象没有部署Iterator 接口,无法用for...of遍历,数组部署了Iterator 接口。

for...in循环有几个缺点。

  • 数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
  • for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
  • 某些情况下,for...in循环会以任意顺序遍历键名。

总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。 for...of循环相比上面几种做法,有一些显著的优点。

  • 有着同for...in一样的简洁语法,但是没有for...in那些缺点。
  • 不同于forEach方法,它可以与breakcontinuereturn配合使用。
  • 提供了遍历所有数据结构的统一操作接口。

 // for...in  遍历默认拿到键名
  var arr = ['a', 'b', 'c', 'd'];
  for (let a in arr) {
      console.log(a); // 0 1 2 3
  }

  // for...of  遍历默认拿到键值
  for (let a of arr) {
      console.log(a); // a b c d
  }