最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • javascript之Array对象回顾

    正文概述 掘金(jayray)   2020-12-20   415

      最近在回顾javascript的知识点,花了几天的空余时间重新学习了一下MDN中的Array对象,对Array对象有了新的认识和理解,所以决定将这些温故知新的内容大致的整理一下并记录下来,也在于鞭策自己继续学习。

      首先,我们知道Array是引用数据类型,其本质是保存在堆内存中的对象,在栈内存中保存的是对象在堆内存中的引用地址。MDN中数组的描述如下:数组是一种类列表对象。数组对象有三个属性:lengthconstructorprotoType

    • length:表示数组的长度,即数组所包含的元素个数
    • constructor:表示创建数组对象的构造函数,所有数组实例的constructor都是Array
    • protoType:表示数组实例的原型对象

      数组对象的方法有很多,这里笔者根据自己的理解归纳了9大类,逐一针对各方法的语法及使用做了简要的介绍。

    1.数组的创建

      我们在使用js创建数组时,通常会通过数组直接量形式或构造函数Array来创建,而ES6新增了两种创建数组的方法:Array.ofArray.from

    • 数组直接量形式创建

      const arr = []; // 空数组
      const arr1 = [1,2,3,4,5];
      
    • 构造函数Array实例化

      // 1.直接实例化
      const arr = new Array(1,2,3,4,5);
      console.log(arr); // [1,2,3,4,5]
        
      // 2.实例化后赋值
      const arr1 = new Array(); 
      arr1[0] = 1;
      arr1[1] = 2;
      arr1[2] = 3;
      arr1[3] = 4;
      arr1[4] = 5;
      console.log(arr1); // [1,2,3,4,5]
      

      使用Array构造函数创建数组时,会存在一个比较奇怪的现象:构造函数在传入单个参数为整数时,所创建的数组为空数组,且数组的长度为该整数,若传入的单个参数为字符串时,所创建的数组才是包含该参数的长度为1的数组。

    // 构造函数Array传入单个整数5时,此时创建的数组为[],且length为5
    const arr = new Array(5);
    console.log(arr)    // [,,,,,] 这里是指5个空位的空数组
    console.log(arr[0]) // undefined
    
    // 构造函数Array传入单个参数为'5',此时创建的数组为['5'];
    const arr1 = new Array('5');
    console.log(arr1) // ['5']
    

      为了避免Array构造函数创建数组时产生的歧义,ES6新增了Array.of()方法来创建数组

    • Array.of()方法创建

      语法:Array.of(...args),任意个参数按顺序成为创建数组的元素

      Array.of()方法可以创建可变参数的数组实例,其不必考虑参数的数量或类型,所创建的数组元素即为所传入的参数。

      const arr = Array.of(5);
      console.log(arr)  // [5]
      const arr1 = Array.of(1,2,3,4,5);
      console.log(arr1) // [1,2,3,4,5]
      const arr2 = Array.of(undefined);
      console.log(arr2) // [undefined]
      

      ES6还新增了Array.from()方法,可以从类数组或可迭代对象创建一个新的,浅拷贝的数组实例。

    • Array.from()方法创建

      语法:Array.from(arrayLike,mapFn,thisArg)

      其中,arrayLike为类数组对象(拥有length属性和若干索引属性的对象)或可迭代对象(SetMaparguments等),mapFn为可选参数,创建的新数组的每个元素都会执行该回调函数,thisArg可选参数,指定执行mapFnthis对象。

      // 1.String生成数组
      const str = 'string';
      const arr = Array.from(str);
      console.log(arr); // ['s','t','r','i','n','g']
        
      // 2.Set生成数组
      const set = new Set(['s','e','t']);
      const arr1 = Array.from(set);
      console.log(arr1); // ['s','e','t']
        
      // 3.Map生成数组
      const map = new Map([[1,'m'],[2,'a'],[3,'p']]);
      const arr2 = Array.from(map);
      const arr3 = Array.from(map.values());
      const arr4 = Array.from(map.keys());
      console.log(arr2); // [[1,'m'],[2,'a'],[3,'p']]
      console.log(arr3); // ['m','a','p']
      console.log(arr4); // [1,2,3];
        
      // 4.类数组对象arguments生成数组
      function f() {
        return Array.from(arguments);
      }
      console.log(f(1,2,3,4,5)); // [1,2,3,4,5]
        
      // 5.可迭代对象生成数组
      const numbers = {
        *[Symbol.iterator](){
          yield 1;
          yield 2;
          yield 3;
        }
      }
      const arr5 = Array.from(numbers,value => value +1);
      console.log(arr5); // [2,3,4]
        
      // 6.指定回调函数
      const arr6 = Array.from([1,2,3,4,5], item => item * 2);
      console.log(arr6); // [2,4,6,8,10]
        
      // 7.指定回调函数及执行回调函数的this对象
      const helper = {
        diff:1,
        add(value) {
          return value + this.diff;
        }
      }
      const arr7 = Array.from([1,2,3,4,5],helper.add,helper); 
      console.log(arr7); // [2,3,4,5,6]
        
      // 8.应用:利用Array.from和Set实现数组去重
      function unique(arr) {
        return Array.from(new Set(arr));
      }
      console.log(unique([1,1,2,2,3,3,4,4,5,5])); // [1,2,3,4,5]
      
    2.内置迭代器

      ES6中有3种类型的集合对象:ArrayMap集合和Set集合,这三种集合对象包括其它可迭代对象都内置了3种迭代器:

    1. entries(): 返回一个迭代器,其值为多个键值对
    2. keys():返回一个迭代器,其值为集合的所有键名
    3. values():返回一个迭代器,其值为集合的值

      了解迭代器的概念就会知道,迭代器对象都有一个next()方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value,表示下一个将要返回的值;另一个是done,一个布尔值,当没有可返回数据时返回true

    • entries()迭代器

      调用entries()返回的迭代器每次调用next()方法时,都返回一个结果对象,对象中的value为一个数组,数组中的元素为集合中每个元素的键和值。若遍历的对象是数组,则第一个元素是数字类型的索引。若为Set集合,则第一个元素和第二个元素都是值。若为Map集合,第一个元素为键名。这里只介绍数组的示例。

      const colors = ['red','yellow','blue'];
      const iter = colors.entries();
      console.log(iter.next()); // { value: [0,'red'], done: false}
      console.log(iter.next()); // { value: [1,'yellow'], done: false}
      console.log(iter.next()); // { value: [2,'blue'], done: false}
      console.log(iter.next()); // { value: undefined, done: true}
      
    • keys()迭代器

      调用keys()返回的迭代器每次调用next()方法,返回的结果对象中的value为集合中存在的元素的键名。

      const colors = ['red','yellow','blue'];
      const iter1 = colors.keys();
      console.log(iter1.next()); // { value: 0, done: false}
      console.log(iter1.next()); // { value: 1, done: false}
      console.log(iter1.next()); // { value: 2, done: false}
      console.log(iter1.next()); // { value: undefined, done: true}
      
    • values()迭代器

      调用values()返回的迭代器每次调用next()方法,返回的结果对象中的value为集合中所存元素的值。

      const colors = ['red','yellow','blue'];
      const iter2 = colors.values();
      console.log(iter2.next()); // { value: 0, done: false}
      console.log(iter2.next()); // { value: 1, done: false}
      console.log(iter2.next()); // { value: 2, done: false}
      console.log(iter2.next()); // { value: undefined, done: true}
      

      另,ArraySet集合的默认迭代器为values()Map集合的默认迭代器是entries(),在 for...of循环中,若没有显式指定则会使用默认迭代器。

    3.数组的检测与数组元素的检测

      数组的检测方法有Array.isArray,数组元素的检测方法有every()some()

    • Array.isArray():用于检测一个对象是否是Array对象

      语法:Array.isArray(obj)

      若传入的对象是Array,则返回true,否则返回false

      console.log(Array.isArray([])); // true
      console.log(Array.isArray([1])); // true
      console.log(Array.isArray(new Array())); // true
      console.log(Array.isArray({})); // false
      console.log(Array.isArray(null)); // false
      console.log(Array.isArray(undefined)); // false
      
    • every():检测数组的所有元素是否都能通过指定函数的测试,返回一个boolean

      语法:arr.every(callback(element, index, array), thisArg)

      其中,参数element为用于检测的当前值;index可选,为当前值的索引;array可选,为调用every的当前数组;thisArg为执行callback时使用的this对象

      [18,32,4,120,60].every(x => x > 10); // false
      [18,32,14,120,60].every(x => x > 10); // true
      
    • some():检测数组中是否有一个元素能通过指定函数的测试,返回一个boolean

      语法:arr.some(callback(element, index, array), thisArg)

      [2,1,4,8,7].some(x => x > 10); // false
      [2,1,14,8,7].some(x => x > 10); // true
      
    4.数组的重排序

      数组的重排序有两种方法:sort()reverse()

    • sort():用原地算法对数组的元素进行排序,并返回数组。默认的排序顺序是将元素转换成字符串,然后根据逐个字符的unicode位点进行排序,该方法会改变原数组。

      语法:arr.sort(compareFn(firstEl, secondEl))

      其中,compareFn可选,用于指定按某种顺序进行排列的函数。若a和b是两个将要被比较的元素

      • 若`compareFn(a, b) < 0,那么a会排在b前面
      • 若compareFn(a, b) = 0,那么a和b的相对位置不变
      • 若compareFn(a, b) > 0,那么b会排在a前面
      // 数组的升序、降序排序
      const arr = [1,3,2,5,4];
      console.log(arr.sort((a, b) => a - b)); // [1,2,3,4,5]
      console.log(arr.sort((a, b) => b - a)); // [5,4,3,2,1]
      

      我们知道,常见的有八大排序算法:

      稳定的:冒泡排序 O(n2)、插入排序 O(n2)、基数排序 O(logRB)、归并排序 O(nlogn)

      不稳定的:选择排序 O(n2)、希尔排序 O(nlogn)、快速排序 O(nlogn)、堆排序 O(nlogn)

      这里笔者比较好奇sort()排序的底层究竟做了啥,它的算法时间复杂度又是多少呢?

      不同浏览器的sort()方法的实现不同,这里主要研究了V8引擎下的sort()方法的源码,发现对于长度<=10的数组使用的是插入排序,>10的数组使用的是插入排序+快速排序。这里截取了源码的一段:

    // Insertion sort is faster for short arrays.
    if (to - from <= 10) {
      InsertionSort(a, from, to);
      return;
    }
    if (to - from > 1000) {
      third_index = GetThirdIndex(a, from, to);
    } else {
      third_index = from + ((to - from) >> 1);
    }
    

      当然,这是chrome 70以前sort()的算法实现,从chrome 70开始,V8团队更新了sort()方法,使用了Timesort算法,这里笔者未作深究。

    • reverse():将数组中的元素颠倒,并返回该数组。该方法会改变原数组

      语法:arr.reverse()

      const arr = [1,2,3,4,5];
      console.log(arr.reverse()); // [1,2,3,4,5]
      
    5.数组元素的操作

      针对数组的元素主要有数组元素的添加,删除和修改等操作,方法包括:

      添加:push()unshift()

      删除:pop()shift()

      修改:fill()copyWithin()

    • push():将一个多个元素添加到数组的末尾,并返回该数组的新长度

      语法:arr.push(element1,...,elementN)

      push方法具有通用性,可以和call()apply()一起使用,应用在类数组对象上。

      // 添加元素到数组
      const arr = [1,2,3,4,5];
      const total = arr.push(6,7,8,9,10);
      console.log(arr); // [1,2,3,4,5,6,7,8,9,10]
      console.log(total); // 10 total为数组的新长度值
        
      // 利用apply()合并两个数组
      const arr1 = [1,2,3,4,5];
      const arr2 = [6,7,8,9,10];
      Array.prototype.push.apply(arr1,arr2);
      console.log(arr1); // [1,2,3,4,5,6,7,8,9,10]
        
      // 类数组对象的应用
      const obj = {
        length: 0,
        addElem: function(elem){ [].push.call(this, elem) } 
      };
      obj.addElem({});
      obj.addElem({});
      console.log(obj); // { 0: {}, 1: {}, length: 2, addElem: ƒ}
      
    • pop():删除数组中的最后一个元素,并返回该元素。当数组为空时,返回undefined

      语法:arr.pop()

      pop方法也具有通用性,可以和call()apply()一起使用,应用在类数组对象上。

      // 删除数组的最后一个元素
      const arr = [1,2,3,4,5];
      const elem = arr.pop();
      console.log(arr); // [1,2,3,4]
      console.log(elem); // 5
      
    • unshift():将一个多个元素添加到数组的开头,并返回该数组的新长度

      语法:arr.unshift(element1,...,elementN)

      unshift同样可以通过call()apply()方法作用于类数组对象上。

      // 添加元素到数组
      const arr = [1,2,3,4,5];
      const total = arr.unshift(6,7,8,9,10);
      console.log(arr); // [6,7,8,9,10,1,2,3,4,5]
      console.log(total); // 10
        
      // 合并数组
      const arr1 = [1,2,3,4,5];
      const arr2 = [6,7,8,9,10];
      Array.prototype.unshift.apply(arr1,arr2);
      console.log(arr1); // [6,7,8,9,10,1,2,3,4,5];
      
    • shift():删除数组中的第一个元素,并返回该元素。当数组为空时,返回undefined

      语法:arr.shift()

      shift也同样可以通过call()apply()方法作用于类数组对象上。

      const arr = [1,2,3,4,5];
      const elem = arr.shift();
      console.log(arr); // [2,3,4,5]
      console.log(elem); // 1
      

      我们知道,栈的特点是后进先出,队列的特点是后进后出。因此,我们可以利用数组的push()pop()实现栈结构,push()shift()实现队列结构。

    // 栈结构 后进先出
    const Stack = function() {
        this.data = [];
        this.insert = push;
        this.delete = pop;
        this.clear = clear;
        this.length = length;
    }
    const push = function(element) {
        this.data.push(element);
        return this.data.length;
    }
    const pop = function() {
        return this.data.pop();
    }
    const clear = function() {
        this.data.length = 0;
    }
    const length = function() {
        return this.data.length;
    }
    
    const s = new Stack();
    s.insert('first');
    s.insert('second');
    console.log(s.data); // ['first','second']
    s.delete();
    console.log(s.data); // ['first']
    s.clear();
    console.log(s.data); // []
    
    // 队列结构 后进后出
    const Queue = function() {
        this.data = [];
        this.insert = push;
        this.delete = shift;
        this.clear = clear;
        this.length = length;
    }
    const push = function(element) {
        return this.data.push(element);
    }
    const shift = function() {
        return this.data.shift();
    }
    const clear = function() {
        this.data.length = 0;
    }
    const length = function() {
        return this.data.length;
    }
    
    const q = new Queue();
    q.insert('first');
    q.insert('second');
    console.log(q.data); // ['first','second']
    q.delete();
    console.log(q.data); // ['second']
    q.clear();
    console.log(q.data); // []
    

      再进一步思考,我们可以通过栈来实现DFS:首先将根节点入栈,然后出栈后检查此节点是否有子节点,有子节点就逆序推入栈中,再出栈,子节点逆序入栈,依次循环;通过队列实现BFS:首先将根节点入队列,然后出队列检查此节点是否有子节点,有子节点就顺序入队列,再出队列子,节点顺序入队列,依次循环

    // 栈实现DFS
    function deepTraversal(node) {
        const nodelist = [];
        if(node) {
            const stack = [];
            stack.push(node); // 根节点入栈
            while(stack.length !== 0) {
                const childItem = stack.pop(); // 出栈(数组的最后一个元素)
                nodelist.push(childItem); 
                const childrenList = childItem.children;// 获取出栈的节点的子节点,保证子节点先出栈
                for(let i = childrenList.length-1;i >= 0;i--) {
                    stack.push(childrenList[i]); // 子节点逆序入栈
                }
            }
            return nodelist;
        }
    }
    
    // 队列实现BFS
    function wideTraversal(node) {
        const nodelist = [];
        if(node) {
            const queue = [];
            queue.push(node); // 根节点入队列
            while(queue.length !== 0) {
                const childItem = queue.shift(); // 出队列(数组第一个元素)
                nodelist.push(childItem);
                const childrenList = childItem.children; // 获取出队列的节点的子节点
                for(let i = 0;i <= childrenList.length -1;i++) {
                    queue.push(childrenList[i]); // 子节点顺序入队列,保证父节点先出队列
                }
            }
            return nodelist;
        }
    }
    
    • fill():用一个固定值填充一 个数组从起始索引到终止索引(即替换数组的指定位置的元素),不包括终止索引。返回修改后的数组。

      语法:arr.fill(value, start, end)

      value为用来填充数组元素的值。start可选,为起始索引,默认值为0,当start为负数时,开始索引为length+startend可选,为终止索引,默认值为length,当end为负数时,终止索引为length+end

      fill为通用方法,类数组对象可通过call()apply()来调用fill

      const arr1 = [1,2,3,4,5].fill(4);
      const arr2 = [1,2,3,4,5].fill(4,1,2);
      const arr3 = [1,2,3,4,5].fill(4,4,4);
      const arr4 = [1,2,3,4,5].fill(4,-2);
      const arr5 = [1,2,3,4,5].fill(4,-4,-1);
      const arr6 = Array(5).fill(4);
      const arr7 = [].fill.call({length: 5},4);
      console.log(arr1);   // [4,4,4,4,4]
      console.log(arr2);   // [1,4,3,4,5]
      console.log(arr3);   // [1,2,3,4,5]
      console.log(arr4);   // [1,2,3,4,4]
      console.log(arr5);   // [1,4,4,4,5]
      console.log(arr6);   // [4,4,4,4,4]
      console.log(arr7);   // {0: 4, 1: 4, 2: 4, 3: 4, 4: 4, length: 5}
      
    • copyWithin():浅拷贝数组的一部分到同数组中的另一个位置,并返回它,不会改变原数组的长度。返回改变后的数组。

      语法:arr.copyWithin(target, start, end)

      target为复制序列到该索引的位置。当target为负数时,复制到的位置索引为length+target,当target大于length时,不发生拷贝,当targetstart之后,复制的序列将被修改以符合arr.length

      start为开始复制元素的起始索引。默认为0,当start为负数时,开始的索引为length+start

      end为开始复制元素的终止索引,不包括该位置的元素。默认为arr.length,当end为负数时,终止的索引为length+end

      const arr1 = [1,2,3,4,5].copyWithin(-2);
      const arr2 = [1,2,3,4,5].copyWithin(0,3);
      const arr3 = [1,2,3,4,5].copyWithin(0,3,4);
      const arr4 = [1,2,3,4,5].copyWithin(3,1,4);
      const arr5 = [1,2,3,4,5].copyWithin(1,-3,-1);
      const arr6 = [1,2,3,4,5].copyWithin(1,-3,-4);
      const arr7 = [].copyWithin.call({length: 5, 3: 1},0,3);
      console.log(arr1);   // [1,2,3,1,2]
      console.log(arr2);   // [4,5,3,4,5]
      console.log(arr3);   // [4,2,3,4,5]
      console.log(arr4);   // [1,2,3,2,3]
      console.log(arr5);   // [1,3,4,4,5]
      console.log(arr6);   // [1,2,3,4,5]
      console.log(arr7);   // {0: 1, 3: 1, length: 5}
      
    6.数组元素的查找和过滤

      js中数组针对数组某个元素的查找,主要有indexOf()lastIndexOf()以及ES6新增的includes()find()findIndex()方法;filter()可以针对数组查找符合某个条件的所有元素。

    • indexOf():返回指定元素在数组中的第一个的索引,若不存在,则返回-1。从数组的前面向后查找

      语法:arr.indexOf(searchElment , fromIndex:option)

      其中,searchElement为要查找的元素,fromIndex可选,为开始查找的位置,fromIndex为负数时,表示从倒数第|fromIndex|个元素开始查找(也可以顺数fromIndex+arr.length),查找顺序仍然是从前向后。

      // 查找元素在数组中的位置
      const arr = [1,2,3,4,5];
      console.log(arr.indexOf(2));     //  1
      console.log(arr.indexOf(6));     // -1
      console.log(arr.indexOf(3,3));   // -1
      console.log(arr.indexOf(2,-1));  // -1
      console.log(arr.indexOf(4,-3));  // 3
      
    • lastIndexOf():返回指定元素在数组中的最后一个的索引,若不存在,则返回-1。从数组的后面向前查找。

      语法:arr.lastIndexOf(searchElement, fromIndex)

      searchElement为要查找的元素,fromIndex可选,为从此位置开始逆序查找,当fromIndex为负数时,表示从数组倒数第|fromIndex|个元素开始查找(也可以顺数fromIndex+arr.length),查找顺序仍然是从后往前。

      const arr = [1,2,3,4,5,3,1];
      console.log(arr.lastIndexOf(3));     //  5
      console.log(arr.lastIndexOf(6));     // -1
      console.log(arr.lastIndexOf(3,3));   // 2
      console.log(arr.lastIndexOf(3,-3));  // 2
      console.log(arr.lastIndexOf(3,-1));  // 5
      
    • includes():查找一个数组是否包含指定元素,若包含则返回true,否则返回false

      语法:arr.incluedes(searchElement, fromIndex)

      searchElment为要查找的元素,fromIndex可选,从fromIndex索引处开始查找,当fromIndex为负数时,则从arr.length+fromIndex的索引开始查找(或倒数第|fromIndex|个元素开始),若计算出来的索引仍然小于0,则整个数组都会搜索,查找顺序仍然时从前向后。

      arr = [1,2,3,4,5];
      console.log(arr.includes(1));      // true
      console.log(arr.includes(3,2));    // true
      console.log(arr.includes(3,-2));   // false
      console.log(arr.includes(5,-10));  // true
      
    • find():返回数组中满足提供测试的测试函数的第一个元素的值,若没有则返回undefined

      语法:arr.find(callback(element, index, array),thisArg)

      其中,callback为在数组每一项上执行的函数,有3个参数:element为当前遍历的元素;index可选,为当前遍历的索引;array也是可选,为调用findIndex的数组。thisArg可选,为执行回调时的this对象。

      // 用对象的属性查找数组里的某个对象
      const inventory = [
        {name: 'apples', quantity: 2},
        {name: 'bananas', quantity: 0},
        {name: 'cherries', quantity: 5}
      ]
      console.log(inventory.find(fruit => fruit.name === 'cherries')); 
      // {name: 'cherres', quantity: 5}
        
      // 寻找数组中的首个质数
      function isPrime(element) {
        let start = 2;
        while(start <= Math.sqrt(element)) {
          if(element % start++ < 1) {
            return false;
          }
        }
        return element > 1;
      }
      console.log([4,6,8,12].find(isPrime));      // undefined
      console.log([4,5,8,12].find(isPrime));      // 5
      console.log([3,4,5,7,8,12].find(isPrime));  // 3
      
    • findIndex():返回数组中满足提供的测试函数的第一个元素的索引。若没有则返回-1。

      语法:arr.findIndex(callback(element, index,array),thisArg)

      callback为在数组每一项上执行的函数,有3个参数:element为当前遍历的元素;index可选,为当前遍历的索引;array可选,为调用findIndex的数组。thisArg可选,为执行callback时的this对象。

      // 查找数组中首个质数元素的索引
      function isPrime(element) {
        let start = 2;
        while(start <= Math.sqrt(element)) {
          if(element % start++ < 1) {
            return false;
          }
        }
        return element > 1;
      }
      console.log([4,6,8,12].findIndex(isPrime));    // -1
      console.log([4,6,7,12].findIndex(isPrime));    // 2
      console.log([4,5,6,7,12].findIndex(isPrime));  // 1
      
    • filter():创建一个新数组,其包含通过所提供函数的测试的所有元素。

      语法:const newArray = arr.filter(callback(element,index,array),thisArg)

      其中,callback为测试数组的每个元素的函数。返回true表示该元素通过测试,保留该元素,false则不保留。有3个参数:element为当前元素;index为当前索引;array为调用filter的数组。thisArg为执行callback时的this对象。

      // 筛选满足条件的元素
      const arr = [12,5,10,24,48];
      const arr1 = arr.filter(element => element > 10);
      const arr2 = arr.filter(element => element <= 10);
      console.log(arr1); // [12,24,48]
      console.log(arr2); // [5,10]
        
      // 过滤json中的无效条目
      const arr = [
        { id: 15 },
        { id: -1 },
        { id: 0 },
        { id: 3 },
        { id: 12.2 },
        { },
        { id: null},
        { id: NaN },
        { id: 'undefiend' }
      ];
      let invalidEntries = 0;
      function filterByID(item) {
        if(item.id !== undefined 
           && typeof(item.id) === 'number' 
           && !isNaN(item.id) 
           && item.id !== 0) {
          return true;
        }
        invalidEntries++;
        return false;
      }
      const newArr = arr.filter(filterByID);
      console.log(newArr);          // [{id: 15},{id: -1},{id: 3},{id: 12.2}]
      console.log(invalidEntries);  // 5
      
    7.数组的合并与切割

      js提供了数组的合并和分割的方法,可以针对一个或多个数组进行操作。

      合并的方法有:concat()join()

      切割的方法有:slice()splice()

    • concat():合并两个或多个数组。此方法不改变原数组,返回一个新数组。

      语法:const newArray = oldArray.concat(Array1,...,ArrayN)

      concat的参数为空,则返回调用此方法的现有数组的一个浅拷贝(即oldArray的浅拷贝)。

      // 合并多个数组
      const arr1 = [1,2,3];
      const arr2 = [4,5,6];
      const arr3 = ['a','b','c'];
      const arr = arr1.concat(arr2,arr3);
      console.log(arr); // [1,2,3,4,5,6,'a','b','c']
        
      // 返回原数组的浅拷贝
      const oldArray = [
        {
          person: {
          	name: 'zhang',
         		age: 28
        	},
          city: 'wuhan'
        },
        {
          person: {
            name: 'wang',
            age: 29
          },
          city: 'shenzheng'
        }
      ]
      const newArray = oldArray.concat();
      oldArray[0].person.age = 27;
      console.log(newArray); 
      // [{person: {name:'zhang',age:27},city:'武汉'},{person:{name:'wang',age:29},city:'深圳'}]
      
    • join():按指定分隔符合并数组或类数组对象的所有元素,连接成一个字符串并返回。若数组只有一个项目,那么返回时不使用分隔符。

      语法:arr.join(separator)

      separator可选,为指定连接数组每个元素的分隔符。若缺省该值,数组元素以逗号(,)分隔,若separator为空字符串(''),则数组元素之间没有任何字符。如果一个元素为undifinednull,它会被转换为空字符串。

      // 连接数组
      const arr = ['Winter',undefined,'Is',null,'Comming'];
      const str = arr.join();
      const str1 = arr.join(',');
      const str2 = arr.join('_');
      const str3 = arr.join('');
      console.log(str);    // Winter,,Is,,Comming
      console.log(str1);   // Winter,,Is,,Comming
      console.log(str2);   // Winter__Is__Comming
      console.log(str3);   // WinterIsComming
        
      // 连接类数组对象
      function f(a,b,c) {
        const s = Array.prototype.join.call(arguments);
        console.log(s); 
      }
      f(1,'cold',true); // 1,cold,true
      
    • slice():返回一个新的数组对象,这个对象是由beginend决定的原数组的浅拷贝(包含begin,不包含end)。原数组不改变。

      语法:arr.slice(begin, end)

      begin可选,指从该索引开始切割原数组元素。若省略begin,则slice从索引0开始,若begin为负数,则表示从倒数第几个元素开始切割(或顺数索引arr.length+begin开始)。

      end可选,指在该索引处结束切割原数组元素。若end省略,则slice提取到原数组的末尾,若end为负数,则表示在原数组的倒数第几个元素结束(或顺数索引arr.length+end结束),若end大于数组的长度,slice也会提取到原数组的末尾。

      // 返回现有数组的一部分
      const arr = ['Banana','Orange','Lemon','Apple','Mango'];
      const fruit = arr.slice();
      const fruit1 = arr.slice(1,3);
      const fruit2 = arr.slice(-1,3);
      const fruit3 = arr.slice(3,-1);
      const fruit4 = arr.slice(-1,-3);
      const fruit5 = arr.slice(-3,-1);
      console.log(fruit);    // ['Banana','Orange','Lemon','Apple','Mango']
      console.log(fruit1);   // ['Orange','Lemon']
      console.log(fruit2);   // []
      console.log(fruit3);   // ['Apple']
      console.log(fruit4);   // []
      console.log(fruit5);   // ['Lemon','Apple']
        
      // 类数组对象调用
      function list() {
        return [].slice.call(arguments);
      }
      list(1,2,3,4,5);   // [1,2,3,4,5]
      
    • splice():通过删除或替换现有数组的元素或原地添加新的元素来修改数组,并以数组的形式返回被修改的内容。此方法会改变原数组。返回值为被删除的元素组成的数组。

      语法:arr.splice(start, deleteCount, item1,item2,...)

      其中,start为指定修改的开始位置(包括该位置的元素),若超过了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组倒数第几个元素开始,若负数的绝对值大于数组的长度,则表示开始的位置为第0位。

      deleteCount可选,表示要移除的数组元素的个数。当deleteCount大于start之后的元素总数,则start后面的元素都被删除,若deleteCount省略或大于等于length-start,那么start后面的元素都被删除,当deleteCount为0或负数,则不移除元素,这种情况至少应该添加一个新元素。

      item1,item2,...表示要添加进数组的元素,从start位置开始,如果不指定,则splice只删除元素。

      const arr1, arr2, arr3, arr4, arr5 = [1,2,3,4,5];
        
      console.log(arr1.splice(2));          // [3,4,5]
      console.log(arr1);                    // [1,2]
        
      console.log(arr2.splice(1,5));        // [2,3,4,5]
      console.log(arr2);                    // [1]
        
      console.log(arr3.splice(-1,3,4,5));   // [5]
      console.log(arr3);                    // [1,2,3,4,4,5]
        
      console.log(arr4.splice(-1,-1,4,5));  // []
      console.log(arr4);                    // [1,2,3,4,5,4,5]
        
      console.log(arr5.splice(6,2,4,5));    // []
      console.log(arr5);                    // [1,2,3,4,5,4,5]
      
    8.数组的遍历

      js中遍历数组的方法有:forEach()map()reduce()reduceRight()

    • forEach():遍历数组的每个元素,执行一次给定的函数。forEach除非抛出异常,否则无法跳出循环,且不对未初始化的值进行任何操作。返回值为undefined

      语法:arr.forEach(callback(currentValue, index, array), thisArg)

      其中,callback为遍历每个元素执行的函数,该函数有三个参数:

      currentValue为当前遍历的元素;index可选,为当前遍历的索引;array可选,为执行forEach的数组。

      thisArg可选,为执行回调函数时的this对象。

      // 遍历稀疏数组
      const arrSparse = [1,3,,7];
      let numCallbackRuns = 0;
      arrSparse.forEach(function(element) {
        console.log(element);numCallbackRuns++;
      });
      console.log('numCallbackRuns: ',numCallbackRuns);
        
      // 1
      // 3
      // 7
      // numCallbackRuns: 3
      
    • map():创建一个新数组,其结果是遍历数组的每个元素,执行指定的函数后的返回的值。不会改变原数组。

      语法:const newArray = arr.map(callback(currentValue, index, array),thisArg)

      callback为生成新数组元素的函数,该函数有三个参数:

      currentValue为当前遍历的元素;index可选,为当前遍历的索引;array可选,为调用map的数组。

      thisArg为执行callback函数的this对象。

      // 求数组中每个元素的平方
      const arr = [1,2,3,4,5];
      const newArr = arr.map(item => Math.pow(item,2));
      console.log(newArr);    // [1,4,9,16,25]
        
      // map格式化数组中的对象
      const arr = [
        {key: 1, value: 10},
        {key: 2, value: 20},
        {key: 3, value: 30}
      ];
      const newArr = arr.map(function(obj) {
        const rObj = {};
        rObj[obj.key] = obj.value;
        return rObj;
      })
      console.log(newArr); // [{1: 10},{2: 20},{3: 30}]
      
    • reduce():对数组中的每个元素执行一个指定的reducer函数(升序执行),将其结果汇总为单个返回值。

      语法:arr.reduce(callback(accumulator, currentValue, index, array), initialValue)

      其中,callback为遍历每个元素执行的函数,有四个参数:

      accumulator为累计器累计回调的返回值,即上一次调用时返回的累积值。

      currentValue为当前遍历的元素;index可选,为当前遍历的索引;array可选,为调用reduce()的数组。

      initialValue可选,作为第一次调用callback函数时的第一个参数的值,若没有提供,则将使用数组的第一个元素,且遍历跳过该元素。返回值为函数累积处理的结果。

      // 累加数组里的所有值
      const arr = [1,2,3,4,5];
      const total = arr.reduce((acc,cur) => acc + cur, 0); 
      console.log(total);    // 15
        
      // 将二维数组转化为一维数组
      const arr1 = [[1,2],[3,4],[5,6]];
      const flattened = arr1.reduce((acc, cur) => acc.concat(cur), []);
      console.log(flattened);    // [1,2,3,4,5,6]
        
      // 计算数组中每个元素出现的次数
      const names = ['Alice', 'Bob', 'Tiff','Bruce', 'Alice'];
      const countNames = names.reduce(function(allNames, name){
        if(name in allNames){
          allNames[name]++;
        } else {
          allNames[name] = 1;
        }
      	return allNames
      },{});
      console.log(countNames);  // {'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1}
        
      // 数组去重
      const arr2 = [1,3,5,7,5,3,1];
      const newArr = arr2.reduce((acc, cur) => {
        if(!acc.includes(cur)) {
          acc.push(cur)
        } 
        return acc;
      }, []);
      console.log(newArr);    // [1,3,5,6]
      
    • reduceRight():接受一个函数作为累加器,从右到左遍历数组的元素,将其结果汇总为单个值。

      语法:arr.reduceRight(callback(accumulator, currentValue, index, array), initialValue)

      其中,callback为遍历每个元素执行的回调函数,有四个参数:

      accumulator为累加器累计回调的返回值,即上一次调用回调函数的返回值。

      currentValue为当前遍历的元素;index可选,为当前遍历的索引;array为调用callback的数组。

      initialValue可选,为首次调用callback函数时,累加器accumulator的值。若没提供初始值,则将使用数组的最后一个元素作为初始值,且遍历跳过该元素。返回值为累计执行的返回值。

      // 计算数组元素的和
      const arr = [0,1,2,3,4];
      const sum = arr.reduceRight((acc, cur) => acc + cur, 0);
      console.log(sum);    // 10
        
      // 扁平化一个二维数组
      const arr2 = [[0,1],[2,3],[4,5]];
      const flattened = arr2.reduceRight((acc, cur) => acc.concat(cur));
      console.log(flattened);  // [4,5,2,3,0,1]
        
      // reduce与reduceRight的区别
      const arr3 = ['1','2','3','4','5'];
      const arr4 = arr3.reduce((acc, cur) => acc + cur);
      const arr5 = arr3.reduceRight((acc, cur) => acc + cur);
      console.log(arr4);    // '12345'
      console.log(arr5);    // '54321'
      
    9.数组扁平化

      js中,可能存在这样中情况,比如数组中嵌套了数组(即数组的元素仍是数组),当我们想将所有的元素展开组成一个一维数组,即数组扁平化。那么此时就要用到flat()flatMap()方法。

    • flat():按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

      语法:const newArr = arr,flat(depth)

      depth可选,指定要提取嵌套数组的嵌套深度,默认值为1。返回值为扁平化后的新数组。

      // 扁平化嵌套数组
      const arr1 = [1,2,[3,4]];
      const arr2 = arr1.flat();
      console.log(arr2);    // [1,2,3,4]
        
      const arr3 = [1,2,[3,4,[5,6]]];
      const arr4 = arr3.flat();
      const arr5 = arr3.flat(2);
      console.log(arr4);    // [1,2,3,4,[5,6]]
      console.log(arr5);    // [1,2,3,4,5,6]
        
      // 使用Infinity可展开任意深度的嵌套数组
      const arr6 = [1,2,[3,4,[5,6,[7,8,[9,10]]]]];
      const arr7 = arr6.flat(Infinity);
      console.log(arr7);    // [1,2,3,4,5,6,7,8,9,10]
        
      // 扁平化会移除数组空项
      const arr8 = [1,2,,4,5];
      const arr9 = arr8.flat();
      console.log(arr9);    // [1,2,4,5]
      

      当然,我们也可以通过递归 + isArray来扁平化嵌套数组:

      const arr = [1,2,3,[1,2,3,[1,2,3,[1,2,3]]]];
      function flatten(arr) {
        return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur)?flatten(cur):cur),[])
      }
      console.log(flatten(arr)); // [1,2,3,1,2,3,1,2,3,1,2,3]
      
    • flatMap():首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与map连着深度值为1的flat几乎相同,但效率更高。

      语法: const newArr = arr.flatMap(callbakc(cuttrentValue, index, array),thisArg)

      其中,callback为映射每个元素的映射函数,有三个参数:

      currentValue为当前处理的元素;index可选,为当前元素的索引;array可选,为调用flatMap的数组。

      thisArg可选,为执行callback函数时的this对象。

      // map()与flatMap()的区别
      const arr = [1,2,3,4,5];
      const arr1 = arr.map(x => [x * 2]);
      const arr2 = arr.flatMap(x => [x * 2]);
      const arr3 = arr.flatMap(x => [[x * 2]]);
        
      console.log(arr1);  // [[2],[4],[6],[8],[10]]
      console.log(arr2);  // [2,4,6,8,10]
      console.log(arr3);  // [[2],[4],[6],[8],[10]]
      
    【参考文献】:

    起源地下载网 » javascript之Array对象回顾

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元