(66)Wangdao.com第十一天_JavaScript 数组Array

数组 Array

本质上,数组属于一种特殊的对象。typeof 运算符会返回数组的类型是 object

  • 数组的特殊性体现在,它的键名是按次序排列的一组整数(0,1,2...)
    // Object.keys方法返回数组的所有键名。
    
    var arr = ['a', 'b', 'c'];
    Object.keys(arr);    // ["0", "1", "2"]    // 可以看到数组的键名就是整数0、1、2。
  • 数组的键名其实也是字符串。之所以可以用数值读取,是因为非字符串的键名会被转为字符串
  • JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个(232 - 1)个,也就是说length属性的最大值就是 4294967295

与普通对象的区别是 "数组使用数字作为索引 [index] 来操作属性"。index 从 0 开始。

数组的存储性能相对较好。

var arr = ['唐僧','孙悟空','猪八戒','沙和尚','白龙马'];


 

1. 创建数组对象

  • var arr = new Array();    // typeof arr    返回 object

2. 给数组添加对象

  • arr[0] = 1;     // console.log(arr)    打印 "1"
    arr[1] = 11;    // console.log(arr)    打印 "1,11"
    arr[2] = 22;    // console.log(arr)    打印 "1,11,22"

3. 读取数组对象

  • console.log(arr[0]);    // 1
    console.log(arr[1]);    // 11
    console.log(arr[2]);    // 22
    console.log(arr[3]);    // undefined    不会报错

4. 获取数组长度

  • arr.length 总是返回  "最大索引值+1" ,所以尽量避免创建不连续数组
    var arr = [1,2,3];
    console.log(arr.length);    // 返回 3 ,因为有 3 个元素 arr[0],arr[1],arr[2]
    
    arr.[100] = 100;     // 此时数组有4个元素 arr[0],arr[1],arr[2],arr[100]
    console.log(arr.length);    // 但是会返回 101 , 实际只有 4 个元素, 这四个元素以外的其他 97 个元素值都为 undefined, 且用 in 判断时返回 false
  • 清空数组的一个有效方法,就是将 arr.length属性设为 0
    var arr = [ 'a', 'b', 'c' ];
    
    arr.length = 0;
    arr    // []

     

  • b
  • b
  •  

5. 在数组最后添加元素

  • // 根据上一个特性,可以在数组最后追加一个元素
    arr[arr.length] = 666;

     


 

1. 使用字面量来创建数组

  • var arr = [];    // 和 new Array() 效果相同

2. 在创建时指定元素

  • var arr = [0,1,2,3,4];
    
    // 等同于
    var arr = new Array(0,1,2,3,4);

3. 创建一个只有一个元素的数组

  • var arr = [10];    // 此时数组只有1个元素,arr[0] = 10;
    
    
    var arr = new Array(10);    // 此时创建了一个长度为10的数组!!!
    // 每个元素的值都为 undefined

4. 数组中元素可以放任意数据类型的数据

  •  

    var arr = ["Hello","How",1,2,3,null,undefined,true,false];
    
    // 数组元素是对象
    var sun = {name:"孙悟空"};
    arr[arr.length] = sun;
    console.log(arr[arr.length-1].name);    // 将会打印 "孙悟空"
    
    
    // 数组元素是函数
    var arr = [
        function(){console.log("Hello!"},
        function(){console.log("Hi!")}
    ];
    
    // 数组 里面放 数组
    var newArr = [["01","02","03"],[0,1,2],["11","22","33"]];
    
    // 混合着放
    var arr = [
      {a: 1},    // 对象
      [1, 2, 3],    // 数组
      function() {return true;}    // 函数
    ];
    
    arr[0]    // Object {a: 1}
    arr[1]    // [1, 2, 3]
    arr[2]    // function (){return true;}

     

5. 遍历数组

  • for...in 循环不仅可以遍历对象,也可以遍历数组,毕竟数组只是一种特殊对象
    var a = [1, 2, 3];
    
    for (var i in a) {
        console.log(a[i]);    // 1 2 3
    }

     

  • 但是,for...in不仅会遍历数组所有的数字键,还会遍历非数字键。所以,不推荐使用 for...in 遍历数组。
    var a = [1, 2, 3];
    a.foo = true;
    
    for (var key in a) {
        console.log(key);    // 0 1 2 foo
        console.log(key);    // 1 2 3 true
    }

     

  • 数组的遍历可以考虑使用 for循环 while循环
    var a = [1, 2, 3];
    
    // for循环
    for(var i = 0; i < a.length; i++) {
        console.log(a[i]);
    }
    
    // while循环
    var i = 0;
    while (i < a.length) {
        console.log(a[i]);
        i++;
    }
    
    // 逆向遍历
    var l = a.length;
    while (l--) {
        console.log(a[l]);
    }

     

  • 数组的 arr.forEach() 方法,也可以用来遍历数组
    var colors = ['red', 'green', 'blue'];
    colors.forEach(function (color) {
        console.log(color);    // red green blue
    });

     

  • 数组的空位 hole        不影响length属性
    • 当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)
    • 如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的。
      var a = ['a' , , 'c'];
      a.length     // 3
      // 数组的空位是可以读取的,返回undefined
      a[0] // 'a'
      a[1] // undefined
      a[2] // 'c'

      var a = [1, 2, 3,];
      a.length // 3
  • delete 命令删除一个数组成员,会形成空位,并且不会影响length属性
    var a = [1, 2, 3];
    delete a[1];
    
    a[1]    // undefined
    a.length    // 3

     

  • 数组的某个位置是空位,与某个位置是undefined,是不一样的。
    • 如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过
      var a = [, , ,];
      
      a.forEach(function (x, i) {
          console.log(i + '. ' + x);    // 不产生任何输出
      })
      
      for (var i in a) {
          console.log(i);    // 不产生任何输出
      }
      
      Object.keys(a)    // []
    • 如果某个位置是 undefined,遍历的时候就不会被跳过
      var a = [undefined, undefined, undefined];
      
      a.forEach(function (x, i) {
          console.log(i + '. ' + x);
      });
      // 0. undefined
      // 1. undefined
      // 2. undefined
      
      for (var i in a) {
          console.log(i);    // 0 1 2
      }
      
      Object.keys(a)    // ['0', '1', '2']

       

6. 类似数组对象

  • 如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”(array-like object)。
    var obj = {
      0: 'a',
      1: 'b',
      2: 'c',
      length: 3
    };
    
    obj[0] // 'a'
    obj[1] // 'b'
    obj.length // 3
    obj.push('d')    // TypeError: obj.push is not a function
    // 因为它们不具备数组特有的方法。
    // 对象obj没有数组的push方法,使用该方法就会报错。

     

  • 有一个问题,这种length属性不是动态值,不会随着成员的变化而变化
  • 典型的“类似数组的对象”有 函数的arguments对象,以及大多数 DOM 元素集,还有字符串
  • 数组的slice方法可以将 “类似数组的对象” 变成真正的数组
    • var arr = Array.prototype.slice.call(arrayLike);

       

  • 通过call(),可以把 forEach() 嫁接到 arrayLike 上面调用
    • 如:在arguments对象上面调用forEach方法。
      // forEach 方法
      function logArgs() {
          Array.prototype.forEach.call(arguments, function (elem, i) {
              console.log(i + '. ' + elem);
          });
      }
      
      // 等同于 for 循环
      function logArgs() {
          for (var i = 0; i < arguments.length; i++) {
              console.log(i + '. ' + arguments[i]);
          }
      }

       

  • 注意,这种方法比直接使用数组原生的forEach要慢,
  • 所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的forEach方法。
    var arr = Array.prototype.slice.call('abc');
    arr.forEach(function (chr) {
          console.log(chr);    // a b c
    });

     

7. 数组的静态方法

  • Array.isArray()    判断变量是否是数组
    var arr = [1, 2, 3];
    
    typeof arr // "object"
    Array.isArray(arr) // true

     

8. 数组的实例方法

  • .valueOf()    数组的 valueOf方法 返回数组本身。。。不同对象的valueOf方法不尽一致
  • .toString()    数组的toString方法 返回数组的字符串形式
  • .push()    用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组
    • var arr = [];
      
      arr.push(1);    // 1
      arr.push('a');    // 2
      arr.push(true, {});    // 4
      
      // arr = [1, 'a', true, {}]

       

  • .pop()    用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组
    • var arr = ['a', 'b', 'c'];
      
      arr.pop()    // 'c'
      
      // arr = ['a', 'b']

      对空数组使用pop方法,不会报错,而是返回undefined

    • pushpop结合使用,就构成了“后进先出”的栈结构(stack)

 

  • .shift()    用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组
    • shift方法可以遍历并清空一个数组。
      var list = [1, 2, 3, 4, 5, 6];
      var item;
      
      while (item = list.shift()) {
          console.log(item);
      }
      
      list     // []
    • pushshift结合使用,就构成了“先进先出”的队列结构(queue)

 

  • .unshift()    用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组
    • unshift方法可以接受多个参数,这些参数都会添加到目标数组头部

 

  • .join()    以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。
    • var a = [1, 2, 3, 4];
      
      a.join(' ');    // '1 2 3 4'
      a.join(' | ');    // "1 | 2 | 3 | 4"
      a.join();    // "1,2,3,4"
      [undefined, null].join('#')    // '#'
      
      ['a',, 'b'].join('-')    // 'a--b'

      如果数组成员是undefinednull或空位,会被转成空字符串

    • 通过call方法,这个方法也可以用于字符串或类似数组的对象
      Array.prototype.join.call('hello', '-')    // "h-e-l-l-o"
      
      var obj = { 0: 'a', 1: 'b', length: 2 };
      Array.prototype.join.call(obj, '-')    // 'a-b'

       

  • .concat()    用于多个数组的合并。
    • 它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变
    • ['hello'].concat(['world']);    // ["hello", "world"]
      
      ['hello'].concat(['world'], ['!']);    // ["hello", "world", "!"]
      
      [].concat({a: 1}, {b: 2});    // [{ a: 1 }, { b: 2 }]
      
      [2].concat({a: 1});    // [2, {a: 1}]

       

    • 如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用
      • 改变原对象以后,新数组跟着改变

 

  • .reverse()    颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组

 

  • .slice(start, end)    用于提取目标数组的一部分,返回一个新数组,原数组不变
    • 它的第一个参数为起始位置(从0开始),
    • 第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个成员
    • 如果slice方法的参数是负数,则表示倒数计算的位置
    • 如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组。
    • var a = ['a', 'b', 'c'];
      a.slice(-2);    // ["b", "c"]
      a.slice(-2, -1);    // ["b"]
      
      var a = ['a', 'b', 'c'];
      a.slice(4);    // []
      a.slice(2, 1);    // []

       

    • Array.prototype.slice().call()方法的一个重要应用,是 将类似数组的对象转为真正的数组
      Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })    // ['a', 'b']
      
      Array.prototype.slice.call(document.querySelectorAll("div"));
      Array.prototype.slice.call(arguments);

       

  • .splice(start, count, addElement1, addElement2, ...)    
    • 用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组
    • 第一个参数是删除的起始位置(从0开始),起始位置如果是负数,就表示从倒数位置开始删除。
    • 第二个参数是被删除的元素个数,
    • 如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
    • 如果只提供第一个参数,从起始位置开始,删除后面所有元素组成一个数组返回

 

  • .sort()    对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变
    • 数值会被先转成字符串,再按照字典顺序进行比较所以101排在11的前面
    • 如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数
      • 函数本身接受两个参数,表示进行比较的两个数组成员。如果该函数的返回值大于0,表示第一个成员排在第二个成员后面;其他情况下,都是第一个元素排在第二个元素前面。
        [10111, 1101, 111].sort(function (a, b) {
            return a - b;
        });    // [111, 1101, 10111]
        [
            { name: "张三", age: 30 },
            { name: "李四", age: 24 },
            { name: "王五", age: 28  }
        ].sort(function (o1, o2) {
            return o1.age - o2.age;
        });
        
        // [
        //     { name: "李四", age: 24 },
        //     { name: "王五", age: 28  },
        //     { name: "张三", age: 30 }
        // ]

         

  • .map()    方法
    • 将数组的所有成员依次传入参数函数,
    • 然后把每一次的执行结果组成一个新数组返回。。。原数组没有变化
      • var numbers = [1, 2, 3];
        
        numbers.map(function (n) {
            return n + 1;
        });    // [2, 3, 4]
        
        numbers    // [1, 2, 3]

         

    • 该函数调用时,map方法向它传入三个参数:当前成员 elem、当前位置 index数组本身 arr
      • [1, 2, 3].map(function(elem, index, arr) {
          return elem * index;
        });    // [0, 2, 6]

         

    • map方法还可以接受第二个参数,用来绑定回调函数内部的this变量
      • var arr = ['a', 'b', 'c'];
        
        [1, 2].map(function (e) {
            return this[e];
        }, arr)    // ['b', 'c']

         

    • 如果数组有空位,map方法的回调函数在这个位置不会执行,会跳过数组的空位 (不会跳过不会跳过undefinednull)
      • var f = function (n) { return 'a' };
        
        [1, undefined, 2].map(f);    // ["a", "a", "a"]
        [1, null, 2].map(f);    // ["a", "a", "a"]
        [1, , 2].map(f);    // ["a", , "a"]

         

  • .forEach()    
    • map方法很相似,也是对数组的所有成员依次执行参数函数
    • 但是,forEach方法不返回值,只用来操作数据。
    • 这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法
    • 参数是一个函数,该函数同样接受三个参数:当前成员 elem、当前位置 index数组本身 arr
    • forEach方法也可以接受第二个参数,绑定参数函数的 this 变量
    • forEach方法也会跳过数组的空位 , 不会跳过undefinednull
    • var out = [];
      
      [1, 2, 3].forEach(function(elem) {
         this.push(elem * elem);
      }, out);
      
      out    // [1, 4, 9]

       

    • 注意 : 
      • forEach方法无法中断执行,总是会将所有成员遍历完。
      • 如果希望符合某种条件时,就中断遍历,要使用for循环

 

  • .filter()    用于过滤数组成员,满足条件的成员组成一个新数组返回
    • 参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
      • [1, 2, 3, 4, 5].filter(function (elem) {
            return (elem > 3);
        });    // [4, 5]    将大于3的数组成员,作为一个新数组返回
        
        
        var arr = [0, 1, 'a', false];
        
        arr.filter(Boolean);    // [1, "a"]    获取所有布尔值为true的成员

         

    • 函数可以接受三个参数:当前成员 elem、当前位置 index数组本身 arr
      • [1, 2, 3, 4, 5].filter(function (elem, index, arr) {
            return index % 2 === 0;
        });    // [1, 3, 5]

         

    • filter方法还可以接受第二个参数,用来绑定参数函数内部的this变量
      • var obj = { MAX: 3 };
        var myFilter = function (item) {
            if (item > this.MAX) return true;
        };
        
        var arr = [2, 8, 3, 4, 1, 3, 2, 9];
        arr.filter(myFilter, obj);    // [8, 4, 9]

         

  • .some() 和 every()
      • 这两个方法类似“断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件
      • 接受一个函数作为参数,所有数组成员依次执行该函数。然后返回一个布尔值
      • 该函数接受三个参数:当前成员 elem、当前位置 index数组本身 arr
    • some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false
      • var arr = [1, 2, 3, 4, 5];
        arr.some(function (elem, index, arr) {
            return elem >= 3;
        });    // true

         

    • every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false
      • var arr = [1, 2, 3, 4, 5];
        arr.every(function (elem, index, arr) {
            return elem >= 3;
        });    // false

         

    • 注意,对于空数组,some方法返回falseevery方法返回true,回调函数都不会执行

 

  • .reduce()  和  .reduceRight()  
    • 依次处理数组的每个成员,最终累计为一个值。
    • 它们的差别是 : 
      • reduce是从左到右处理(从第一个成员到最后一个成员)
      • reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样
    • // 求出数组所有成员的和
      [1, 2, 3, 4, 5].reduce(function (a, b) { console.log(a, b); return a + b; }); // 1 2 第一次执行, a是数组的第一个成员1b是数组的第二个成员2 // 3 3 第二次执行, a为上一轮的返回值3b为第三个成员3 // 6 4 第三次执行, a为上一轮的返回值6b为第四个成员4 // 10 5 第四次执行, a为上一轮返回值10b为第五个成员5 // 最后结果:15

       

    • reduce() 和 reduceRight() 的第一个参数都是一个函数。该函数接受以下四个参数
      • 累积变量,默认为数组的第一个成员
      • 当前变量,默认为数组的第二个成员
      • 当前位置(默认从0开始)
      • 原数组
      • 只有前两个是必须的,后两个则是可选的
    • 如果要对累积变量指定初值,可以把它放在reduce方法和reduceRight方法的第二个参数
      • [1, 2, 3, 4, 5].reduce(function (a, b) {
            return a + b;
        }, 10);    // 25
        // 指定参数a的初值为10,所以数组从10开始累加,最终结果为25。
        // 注意,这时b是从数组的第一个成员开始遍历

         

    • 由于空数组取不到初始值,reduce方法会报错。这时,加上第二个参数,就能保证总是会返回一个值
    • function substract(prev, cur) {
          return prev - cur;
      }
      // 比较    reduce()    reduceRight()
      [3, 2, 1].reduce(substract) // 0
      [3, 2, 1].reduceRight(substract) // -4

       

    • 还可以用来做一些遍历相关的操作。比如,找出字符长度最长的数组成员
      • function findLongest(entries) {
            return entries.reduce(function (longest, entry) {
                return entry.length > longest.length ? entry : longest;
            }, '');
        }
        
        findLongest(['aaa', 'bb', 'c']);     // "aaa"

         

  • .indexOf()    返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1
    • 还可以接受第二个参数,表示搜索的开始位置
      var a = ['a', 'b', 'c'];
      
      a.indexOf('b');    // 1
      a.indexOf('y');    // -1
      
      ['a', 'b', 'c'].indexOf('a', 1);    // -1    结果为-1,表示没有搜索到

       

  • .lastIndexOf()    返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1
    • var a = [2, 5, 9, 2];
      a.lastIndexOf(2);    // 3

      a.lastIndexOf(7); // -1
      [NaN].indexOf(NaN) // -1
      [NaN].lastIndexOf(NaN) // -1

       

    • 不能用来搜索NaN的位置,即它们无法确定数组成员是否包含NaN

 

  • 有不少返回的还是数组,所以可以链式使用
    • var users = [
          {name: 'tom', email: 'tom@example.com'},
          {name: 'peter', email: 'peter@example.com'}
      ];
      
      users
      .map(function (user) {
          return user.email;
      })
      .filter(function (email) {
          return /^t/.test(email);
      })
      .forEach(function (email) {
          console.log(email);
      });
      // 先产生一个所有 Email 地址组成的数组,然后再过滤出以t开头的 Email 地址,最后将它打印出来

       

posted @ 2018-09-26 18:57  耶梦加德  阅读(172)  评论(0编辑  收藏  举报