资讯专栏INFORMATION COLUMN

「JavaScript」Array方法的详细总结及常用数组操作(附完整示例)

Alfred / 1400人阅读

摘要:数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。利用的解构赋值解构赋值尾递归优化递归非常耗内存,因为需要同时保存成千上百个调用帧,很容易发生栈溢出。而尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。

一.前言

因为在工作当中,经常使用到js的数组,而其中对数组方法的使用也是很频繁的,所以总是会有弄混或者概念不够清晰的状况,所以,写下这篇文章整理一番,本文有对几乎所有数组的方法的介绍,此外还有非常实用的一些数组操作比如乱序去重和斐波那契数列求值等等,总之干货满满~~

二.JS中的Array方法 1.检测数组
   //instanceof 测试某个对象是否由某个指定的构造器创建

     [1,2,3] instanceof Array //true
      "1" instanceof Array // false
   
   //比instanceof更可靠 Array.isArray

    Array.isArray([1,2,3]); //true

   //Object对象的toString()方法,可以返回所创建对象的内部类名

    Object.prototype.toString.call([1,2,3]); //"[object Array]"

    Object.prototype.toString.call("a"); //"[object String]"
2.转换方法
   const test = [1,2,3];

   test.toString(); //"1,2,3"

   test.valueOf(); //[1,2,3]

   //toLocaleString大部分为返回与toString相同的结果,区别之处在于会调用每一项的toLocaleString()方法
   test.toLocaleString([1,2,3]); //[1,2,3];

   const testStr = test.join("|"); //"1|2|3"
   testStr.split("|"); // [1,2,3];
3.栈方法(push和pop 尾部操作)
  const test = new Array();
  const count= test.push("a","b");
  //count为操作完后的数组长度
  console.log(count); //2
  
  const count1 = test.push("c");
  console.log(count1); //3
  
  const item = test.pop();
  console.log(item); //"c"
  
4.队列方法(shift和unshift 头部操作)
   const test = [1,2,3];
   const item = test.shift();
   console.log(item); //1
   console.log(test); //[2,3];
   
5.重排序方法
  const test = [1,2,3];
  const test1 = test.reverse(); // [3,2,1]
  test1.sort(); //[1,2,3]
  
6.操作方法
    //concat会创建当前数组的一个副本再进行操作,不影响原数组
    const test = [1,2,3];
    const test1 = [1,2,3].concat([4,5]);
    
    console.log(test); //[1,2,3]
    console.log(test1); //[1,2,3,4,5]
    
    //slice接受一个或两个参数,返回起始和结束位置之间的项(但不包括最后位置的项),不影响原数组
    const test = [1,2,3,4];
    const test1 = test.slice(0); //[1,2,3,4]
    const test2 = test.slice(1,3); //[2,3]
    
    console.log(test); //[1,2,3,4] 原数组未改变
    
    //splice 可用作删除、插入和替换,改变原数组
    const test = [1,2,3,4,5];
    
    test.splice(1,2); //test为[1,4,5]
    
    test.splice(1,0,"a","b"); //test为[1,"a","b",2,3,,4,5]
    
    test.splice(2,1,"c"); //test为[1,2,"c",4,5]
7.位置方法
     const test = [1,2,3,4,5,4,3,2];
     test.indexOf(4); //3
     
     test.lastIndexOf(4); //5
     test.indexOf("4"); //-1  必须全相等(===)
8.循环方法

1.filter() 对数组的每一项允许给定函数,返回该函数会返回true的项组成数组,不会改变原数组

 const test = [1,2,3,4,5];
 const test1 = test.filter((item) => item > 3);
 
 console.log(test); //[1,2,3,4,5];
 console.log(test1); //[4,5];

2.map() 对数组的每一项执行给定函数,返回每次函数调用的结果组成的数组,不会改变原数组

   const test = [{a:1,b:2},{a:3,b:4},{a:5,b:6}];
   const test1 = test.map((item) => item["a"]);
   
   console.log(test); //[{a:1,b:2},{a:3,b:4},{a:5,b:6}]
   console.log(test1); //[1,3,5]

3.forEach 对数组的每一项运行给定函数,没有返回新数组,没有返回值

         const test = [[1],[2]];
         test.forEach((item) => item.push(1));
         console.log(test); //[[1,1], [2,1]]     
9.其他的循环方法
1) 普通for循环(性能较好)
      const test = [1,2,3,4];
      for(let i=0,len=test.length;i
2) for in 以【任意】顺序遍历一个对象的可枚举属性,所以不太建议用来遍历一个数组,原因如下。
for...in不应该用于迭代一个 Array,其中索引顺序很重要。数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。不能保证for ... in将以任何特定的顺序返回索引。for ... in循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些,即它返回的除了数字索引外还有可能是你自定义的属性名字。
       const person = {work:"coder",age:"24",sex: "female"};
       for(prop in person) {
            console.log(`Jchermy[${prop}]=${person[prop]}`);
        }
        //Jchermy[work]=coder
        //Jchermy[age]=24
        //Jchermy[sex]=female
3)for..of  
语句在可迭代的对象上创建了一个循环(包括Array, Map, Set, 参数对象( arguments) 等等),
对值的每一个独特的属性调用一个将被执行的自定义的和语句挂钩的迭代。
for..of 作为es6中引进的循环,主要是为了补全之前for循环中的以下不足 :     

forEach 不能 break 和 return;

for-in 它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。

 而相比之下for...of可以做到:

与forEach 相比,可以正确响应 break, continue, return。

for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。

for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。

for-of 也支持 Map 和 Set (两者均为 ES6 中新增的类型)对象遍历。

        let test = [3, 5, 7];
        test.foo = "hello";
        
        for (let i in test) {
           console.log(i); // "0", "1", "2", "foo"
        }
        
        for (let i of test) {
           console.log(i); // "3", "5", "7" // 注意这里没有 hello
        }
从以上我们可以看出for..of和for...in的区别

1. for...in循环出的是key,for...of循环出的是value
2. 作用于数组的for-in循环除了遍历数组元素以外,还会遍历自定义属性,比如例子中的foo属性。for...of循环不会循环对象的key,只会循环出数组的value。  

4)do...while 语句一直重复直到指定的条件求值得到假(false)

        let i = 0;
        do {
          i += 1;
          console.log(i);
        } while (i < 5);
        //1
        //2
        //3
        //4
        //5

5) while只要指定的条件为真就会一直执行它的语句块

    let n = 0;
    let x = 0;
    while(n<3) {
       n++;
       x +=n;
       console.log(n,xhdf);
    }
    // 1 1
    // 2 3
    // 3 6
    
三、常用数组操作 1.数组乱序

将一个数组完全打乱,然后返回打乱后的数组。也称为洗牌算法。

1) 利用Math.random()和sort()结合

   const test = [1,2,3,4];
   test.concat().sort(()=> Math.random() - 0.5); // [2, 4, 1, 3]
 

这个方法貌似可以实现我们要的结果,但是实际上它并不完全是随机的,而是越大的数字出现在越后面的概率越大。具体原因可以看这篇文章数组的完全随机排列

2) 遍历原数组,然后随机产生下标,将这个下标的值push到新的数组中,并随即删除这值,注意不是用delete,那样并不会改变数组的长度,效率不高,使用splice较好.

       function shuffle(array) {
          let i,n=array.length,copy=[];
          while(n) {
             i = Math.floor(Math.random()*n--);//n--是先与Math.random相乘再减一
             copy.push(array.splice(i, 1)[0]);
          }
          return copy;
       }
       const test = [1,2,3,4,5];
       console.log(shuffle(test));  //[2, 5, 4, 3, 1]

3)Fisher–Yates shuffle 算法 随机从数组中抽出一个元素,然后与最后个元素交换,相当于把这个随机抽取的元素放到了数组最后面去,表示它已经是被随机过了,同时被换走的那个元素跑到前面去了,会在后续的重复操作中被随机掉。一轮操作过后,下一轮我们只在剩下的n-1个元素也就是数组的前n-1个元素中进行相同的操作,直到进行到第一个。

    function shuffle(array) {
        let i, n=array.length;
        while(n) {
          i = Math.floor(Math.random()*(n--));
          
          t = array[i]
          array[i] = array[n];
          array[n] = t;
          n--;
        }
        return array;
    }
    var test = [1,2,3,4];
    console.log(shuffle(test.concat()));//传入数组的副本
    
2. 求斐波那契序列的某一项的值

ps:这一题的解法有很多种,以下仅列出几种

1) 首先祭出最经典的解法,利用递归求值。

     function fibonacci(n) {
         if(n==0 || n==1 ) {
            return n;
         }
         return fibonacci(n-1)+fibonacci(n-2);
     }
     const test = fibonacci(4); //3

这种方法的问题是很多值会重新求值,效率很低,因此并不推荐。
2)利用ES6的解构赋值

     const fibonacci =(n)=>{
        let a = 0;
        let b= 1;
        let i = 2;
        while(i++ <= n){
          [a, b] = [b, a+b]; //解构赋值
        }
        return b;
     }
     fibonacci(4); //3

3) 尾递归优化

递归非常耗内存,因为需要同时保存成千上百个调用帧,很容易发生‘栈溢出’。但对于尾递归优化来说,由于只存在一个调用帧,所以永远不会发生栈溢出。而尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数.
     function fibonacci(n, n1=0, n2=1){
         if(n <=1) {
            return n2;
         }
     return fibonacci(n-1, n2, n1 + n2);  
    
     }
     fibonacci(6); //8

4)利用缓存值减少重复求值

   function fibonacci(){
     var cache = {
        0:0,
        1:1
     }
     return function _fibonacci(n) {
         return typeof cache[n] === "number" ?
         cache[n]:
         cache[n] = _fibonacci(n-1) + _fibonacci(n-2);
     }  
   }
   const f = fibonacci();
   f(9); //34
3.数组排序

1)最常用的利用sort()排序,仅适用于纯数字数组

  //升序
  function asc(arr){
    return arr.sort((a,b)=> {return a-b;})
  }
  asc([6,3,4,2]);//[2,3,4,6]

    //降序
  function desc(arr){
    return arr.sort((a,b)=> {return b-a;})
  }
  desc([4,3,2,5]); //[5, 4, 3, 2]

2)由对象组成的数组,支持根据对象的某个属性排序

  const sortByProp = (name)=>{
    return (f, s) => {
      var a, b;
      if (typeof f === "object" && typeof s === "object" && f && s) {
         a = f[name];
         b = s[name];
         if(a === b) {
            return 0;
         } else {
            return a < b ? -1 :1;
         }  
     } else {
        throw new TypeError("数组必须由对象组成");
     }
  };
};
const test = [{age:27, name:"xiaomi"},{age:17, name:"amy"},{age: 24, name: "Jchermy"}];
test.sort(sortByProp("age")); //[{age:17, name:"amy"},{age: 24, name: "Jchermy"}, {age:27, name:"xiaomi"}];
4.数组去重

1)利用array_filter()

   function unique(arr){
     let uniqueArr = [];
     uniqueArr = arr.filter((item) =>{
        return uniqueArr.includes(item) ? "" : uniqueArr.push(item);
     })
     return uniqueArr;
   };
   unique([1,2,3,1,6,3,2,7]); //[1,2,3,6,7];

2)利用es6中的Map()

   function unique(arr) {
     const seen = new Map();
     return arr.filter((item) => !seen.has(item) && seen.set(item, 1));
   }
   unique([11,2,3,4,4,2,5]); //[11, 2, 3, 4, 5]

3)利用es6中的Set()

    function unique(arr){
       return [...new Set(arr)]; //将set结构转为数组
    }
   unique([1,2,2,3,7,3,8,5]); //[1, 2, 3, 7, 8, 5]  
5.数组去除空值
function filter_array(array) {    
  return array.filter(item=>item);   
}   

const test = [undefined,undefined,1,"","false",false,true,null,"null"];     
filter_array(test);  //[1, "false", true, "null"]
四.结语

呼,写了好几天的文章终于写完啦,撒花~~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95116.html

相关文章

  • 排序算法分析总结js实现)

    摘要:本文对一些排序算法进行了简单分析,并给出了的代码实现。平均时间复杂度不好分析,它是冒泡排序是稳定的排序算法。冒泡排序是原地排序算法原地排序指的是空间复杂度是的排序算法。归并排序,会将数组从中间分成左右两部分。 本文对一些排序算法进行了简单分析,并给出了 javascript 的代码实现。因为本文包含了大量的排序算法,所以分析不会非常详细,适合有对排序算法有一定了解的同学。本文内容其实不...

    liaoyg8023 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • 手动实现bind函数(MDN提供Polyfill方案解析)

    摘要:被调用时,等参数将置于实参之前传递给被绑定的方法。它返回由指定的值和初始化参数改造的原函数拷贝。一个绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。其实这个思路也是库如何实现继承的方法。他的函数如下最后一步是将的指回。 update: 2018-06-08 原文链接 为什么要自己去实现一个bind函数? bind()函数在 ECMA-262 第五版才被加入;它可能无法在所...

    idisfkj 评论0 收藏0
  • (转)JavaScrit常用数组算法总结

    摘要:原文地址不管是在面试中还是在笔试中,我们都会被经常问到关于数组的一些算法,比方说数组去重数组求交集数组扰乱等等。今天抽点时间把中的一些常用的数组算法做一下总结,以方便大家面试笔试或者日常开发过程中用到。 原文地址:http://www.cnblogs.com/front-... 不管是在面试中还是在笔试中,我们都会被经常问到关于javascript数组的一些算法,比方说数组去重、数组求...

    warnerwu 评论0 收藏0
  • task0002(一)- JavaScript数据类型语言基础

    摘要:不过让流行起来的原因应该是是目前所有主流浏览器上唯一支持的脚本语言。经过测试,数字字符串布尔日期可以直接赋值,修改不会产生影响。再考虑对象类型为或者的情况。对于结果声明其类型。判断对象的类型是还是,结果类型更改。 转载自我的个人博客 欢迎大家批评指正 1. 第一个页面交互 这里最需要学习的老师的代码中,每一部分功能都由函数控制,没有创建一个全部变量。且最后有一个函数来控制执行代码...

    elarity 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<