乐趣区

JavaScript之数组

概念

JavaScript 数组是 JavaScript 对象的特殊形式。数组索引实际上和碰巧是整数的属性名差不多,使用方括号访问数组元素就像用方括号访问对象的属性一样。JavaScript 将指定的数字索引值转换成字符串——索引值 1 变成“1”——然后将其作为属性名来使用。

数组的特别之处在于,当使用小于 2^32 的非负整数作为属性名时数组会自动维护其 length 属性值。通常,数组的实现是经过优化的,用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多。

注意,可以使用负数或非整数来索引数组。这种情况下,数值转换为字符串,字符串作为属性名来用。

数组相关函数

数组遍历方法

  1. for 循环
  2. Array.prototype.forEach()
    forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。没有返回值

    array.forEach(callback(currentValue[, index, array]]), [thisArg])

    实例:遍历并输出所有数组元素

    function logArrayElements(element, index, array) {console.log('a[' + index + '] =' + element);
    }
    
    // 注意索引 2 被跳过了,因为在数组的这个位置没有项
    [2, 5, , 9].forEach(logArrayElements);
    // logs:
    // a[0] = 2
    // a[1] = 5
    // a[3] = 9
  3. Array.prototype.map()
    map() 方法返回一个 新数组 ,数组中的元素为原始数组元素调用函数处理后的值, 不改变原数组

    var newArray = arr.map(callback(currentValue[, index[, array]]) {// Return element for new_array}[, thisArg]);
    

    实例 1:求数组中每个元素的平方根

    var numbers = [1, 4, 9];
    var roots = numbers.map(Math.sqrt);
    // roots 的值为[1, 2, 3], numbers 的值仍为[1, 4, 9]

    实例 2:在一个 String 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:

    var map = Array.prototype.map
    var a = map.call("Hello World", function(x) {return x.charCodeAt(0); 
    })
    // a 的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
  4. Array.prototype.filter()
    filter()方法创建一个 新数组,返回一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

    var newArray = array.filter(callback(element[, index[, array]])[, thisArg])

    实例:使用 filter()创建具有非零 id 的元素的 json。

    var arr = [{ id: 15},
      {id: -1},
      {id: 0},
      {id: 3},
      {id: 12.2},
      { },
      {id: null},
      {id: NaN},
      {id: 'undefined'}
    ];
    
    // 判断当前元素是否为数字
    function isNumber(obj) {return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
    }
    //callback 过滤函数
    function filterByID(item) {if (isNumber(item.id) && item.id !== 0) {return true;} 
      invalidEntries++;
      return false; 
    }
    
    var arrByID = arr.filter(filterByID);
    // Filtered Array:
    // [{id: 15}, {id: -1}, {id: 3}, {id: 12.2}]
  5. Array.prototype.reduce()
    方法对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。
    语法:

    var result = array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

    reduce 为数组中的每一个元素依次执行 callback 函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:

    accumulator 累计器
    currentValue 当前值
    currentIndex 当前索引
    array 数组

    回调函数第一次执行时,accumulator 和 currentValue 的取值有几种情况:
    如果调用 reduce()时提供了 initialValue,accumulator 取值为 initialValue,currentValue 取数组中的第一个值;
    如果没有提供 initialValue,那么 accumulator 取数组中的第一个值,currentValue 取数组中的第二个值。
    如果数组为空且没有提供 initialValue,会抛出 TypeError。
    如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue,或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且 callback 不会被执行。

    reduce()如何运行:
    假如运行下段 reduce()代码:

    [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){return accumulator + currentValue;});

    Callback 被调用四次,每次调用的参数和返回值如下表:

    您还可以提供 Arrow Function 来代替完整的函数。下面的代码将产生与上面的代码中相同的输出:

    [0, 1, 2, 3, 4].reduce((prev, curr) => prev + curr );

    如果你打算提供一个初始值作为 reduce()方法的第二个参数,以下是运行过程及结果:

    [0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {returnaccumulator+ currentValue;}, 10);    // =>20

    实例 1:计算数组中每个元素出现的次数

    var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
    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}

    实例 2: 数组去重

    let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
    let result = arr.sort().reduce(function(init, currentValue){if(init.length === 0 || init[init.length - 1] !== currentValue){init.push(current);
      }
        return init;
    }, []);    // => [1, 2, 3, 4, 5]

    此处额外附上其他数组去重方法以作对比参考

    // 方法一
    var norepeat = funtion(arr){return arr.filter(function(val, index, array){return array.indexOf(val) === index;
        });
    }
    norepeat()
    
    // 方法二
    var set = new Set(arr);

    实例 3: 按顺序运行 Promise

     // Runs promises from array of functions that can return promises
     // in chained manner
     // @param {array} arr - promise arr
     // @return {Object} promise object
     
    function runPromiseInSequence(arr, input) {
      return arr.reduce((promiseChain, currentFunction) => promiseChain.then(currentFunction),
        Promise.resolve(input)
      );
    }
    
    // promise function 1
    function p1(a) {return new Promise((resolve, reject) => {resolve(a * 5);
      });
    }
    
    // promise function 2
    function p2(a) {return new Promise((resolve, reject) => {resolve(a * 2);
      });
    }
    
    // function 3  - will be wrapped in a resolved promise by .then()
    function f3(a) {return a * 3;}
    
    // promise function 4
    function p4(a) {return new Promise((resolve, reject) => {resolve(a * 4);
      });
    }
    
    const promiseArr = [p1, p2, f3, p4];
    runPromiseInSequence(promiseArr, 10)
      .then(console.log);   // 1200
  6. Array.prototype.some()Array.prototype.every()

    some()方法测试是否至少有一个元素可以通过被提供的函数方法。该方法返回一个 Boolean 类型的值。some()被调用时 不会改变数组

    array.some(callback(element[, index[, array]])[, thisArg])

    [image:D9039E51-CDF9-4275-8C0C-25D5C07089CA-1005-000010E6636588E6/ 屏幕快照 2019-07-08 15.13.05.png]

    实例:检测在数组中是否有元素大于 10。

    function isBiggerThan10(element, index, array) {return element > 10;}
    
    [2, 5, 8, 1, 4].some(isBiggerThan10);  // false
    [12, 5, 8, 1, 4].some(isBiggerThan10); // true

    every()方法就像数学中的“针对所有”的量词,当且仅当针对数组中的所有元素调用判定函数都返回 true,它才返回 true。和 some 有相似之处。
    实例:

    a = [1,2,3,4,5];
    a.every(function(x) {return X < 10;}) // => true: 所有的值 <10
    a.every(function(x) {return x % 2 === 0;}) // => false: 不是所有的值都是偶数

js 数组方法

  1. Array.prototype.push()Array.prototype.pop()

    push()方法向数组的末尾添加一个或更多元素,并返回新的长度。
    pop()方法删除并返回数组的最后一个元素。

  2. Array.prototype.unshift()Array.prototype.shift()

    shift()方法删除并返回数组的第一个元素。
    unshift()方法向数组的头部添加一个或更多元素,并返回新的长度。

    注意,当使用多个参数调用 unshift()时它的行为令人惊讶。参数是一次性插入的 (就像 splice() 方法)而非 - - 次 - 一个地插入。这意味着最终的数组中插入的元素的顺序和它们在参数列表中的顺序 - 致。而假如元素是一次一个地插入,它们的顺序应该是反过来的。

  3. Array.prototype.reverse()

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

  4. Array.prototype.sort()
    方法用原地算法对数组的元素进行排序,并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串 Unicode 码点。由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。sort()原数组上原地排序,会导致 原数组改变

    var a = [33, 4, 1111, 222];
    a.sort();
    // 字母表顺序: 1111,222,33,4
    a.sort(function(a, b) {// =>a: [4, 33, 222, 1111]
    return a - b;    // 根据顺序,返回负数、0、正数
    });
    a.sort(function(a, b) {return b - a});    // =>a: [1111, 222, 33, 4]
  5. Array.prototype.concat(array2)
    方法用于合并两个或多个数组。concat()方法 不会更改原数组 ,而是返回一个 新数组
  6. Array.prototype.join()
    方法将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串 。可以指定一个可选的字符串在生成的字符串中来分隔数组的各个元素。如果不指定分隔符,默认使用逗号。join() 方法不该变原数组。
    实例:字符串数组连接成字符串

    var arr = new Array(3)
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    arr.join()
    // output: George,John,Thomas
  7. Array.prototype.slice(start, end)

    slice()方法返回指定数组的一个片段或子数组。它的两个参数分别指定了片段的开始和结束的位置。返回的数组 包含第一个参数 指定的位置和所有到但 不含第二个参数 指定的位置之间的所有数组元素。如果只指定一个参数,返回的数组将包含从开始位置到数组结尾的所有元素。如参数中出现负数,它表示相对于数组中最后一个元素的位置。slice()方法 不会改变原数组

    // 语法:
    str.slice(beginSlice[, endSlice])
    
    beginSlice:
    必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。endSlice:
    可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。// 实例: 
    var arr = new Array(3)
    arr[0] = "George"
    arr[1] = "John"
    arr[2] = "Thomas"
    arr.slice(1) // output: John,Thomas
    
  8. Array.prototype.splice():方法通过删除或替换现有元素或者原地添加新的元素来修改数组, 并以数组形式返回被修改的内容。此方法会 改变原数组
    语法:

    array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

    实例:

    // 示例 1: 添加元素
    var fruits = ["Banana", "Orange", "Apple", "Mango"];
    fruits.splice(2,1,"Lemon","Kiwi");    // output: Banana,Orange,Lemon,Kiwi,Mango
    
    // 示例 2: 删除元素
    var fruits = ["Banana", "Orange", "Apple", "Mango"];
    fruits.splice(2,2);    // output: Banana,Orange
  9. Array.prototype.indexOf()Array.prototype.lastIndexOf()
    index0f() 和 lastIndex0f()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回 -1。index0f()从头至尾搜索,而 lastIndexOf()则反向搜索。
  10. Array.prototype.reduce(reducer)
    方法对数组中的每个元素执行一个由您提供的 reducer 函数 (升序执行),将其结果汇总为 单个返回值
  11. Array.prototype.map():对数组的每一项应用回调函数,返回 新数组
  12. Array.prototype.some():数组中 只需有一项 满足给定条件则返回 true。
  13. Array.prototype.every():数组的 每一项 都满足给定条件则返回 true。
  14. Array.prototype.filter():方法创建一个 新数组, 其包含通过所提供函数实现的测试的所有元素。
  15. Array.prototype.forEach():数组遍历,与 for 循环一样,没有返回值

数组类型

给定一个未知的对象,判定它是否为数组通常非常有用。在 ECMAScript 5 中,可以使用 Array.isArray()函数来做这件事情:

Array.isArray([]) //=>true
Array.isArray({}) // => false

在 ECMAScript 3 中 isArray()函数的代码可以这样书写:

var isArray = Function.isArray || function(o) {return typeof o === "object" && Object.prototype.toString. call(o) === "[object Array]";
}; 

类数组对象

我们已经看到,JavaScript 数组的有 - – 些特性是其他对象所没有的:

* 当有新的元素添加到列表中时,自动更新 length 属性。* 设置 length 为一个较小值将截断数组。* 从 Array.prototype 中继承一些有用的方法。* 其类属性为“Array'。

这些特性让 JavaScript 数组和常规的对象有明显的区别。但是它们不是定义数组的本质特性。一种常常完全合理的看法把拥有 - 个数值 length 属性和对应非负整数属性的对象看做一种类型的数组。

实例:为一个常规对象增加了一些属性使其变成类数组对象,然后遍历生成的伪数组的“元素”:

var a = {};    // 从一个常规空对象开始
// 添加一些属性,称为 "类数组"
var i = 0;
while(i < 10) {a[i] = i * i;
    i++;
}
a.length = i;
// 现在,当做真正的数组遍历它
var total = 0;
for(var j = 0; j < a.length; j++)
total += a[j] ;    // => 287

JavaScript 中的 Arguments 对象就是一个类数组对象。一些 DOM 方法 (如 document.getElementsByTagName() 也返回类数组对象。
下面有一个函数可以用来检测类数组对象:

// 判定 o 是否是一个类数组对象
// 字符串和函数有 length 属性,但是它们
// 可以用 typeof 检测将其排除。在客户端 JavaScript 中,DOM 文 本节点
// 也有 length 属性,需要用额外判断 o. nodeType != 3 将其排除
function isArrayLike(o) {return o && typeof o === "object" && isFinite(o.length) && o.length >= 0 && o.length === Math.floor(o.length) && o.length < 4294967296;             
}

参考:

*《JavaScript 权威指南》第六版
* [MDN Web 文档](https://developer.mozilla.org/zh-CN/)

推荐阅读:
【专题:JavaScript 进阶之路】
JavaScript 之“use strict”
JavaScript 之 new 运算符
JavaScript 之 call() 理解

JavaScript 之对象属性

我是 Cloudy,年轻的前端攻城狮一枚,爱专研,爱技术,爱分享。
个人笔记,整理不易,感谢阅读、点赞和收藏。
文章有任何问题欢迎大家指出,也欢迎大家一起交流前端各种问题!

退出移动版