5.2 Array类型+常用数组操作

@一棵菜菜  May 21, 2019

简介

本文摘抄自《js高级程序设计》"5.2 Array类型" 篇章,这次整理之前‘《js高级程序设计》笔记’时将其单独成文,方便查阅。均是ES5的内容。


大纲(小结)

创建 Array 实例
length 属性
5.2.1 检测数组

Array 常用方法汇总:

[1].isArray():判断对象是否为数组。
5.2.2 转换为字符串(逗号分隔):toString() 不修改原数组
5.2.3 栈方法:push(),pop() 会修改原数组
5.2.4 队列方法:push(),shift() 会修改原数组
5.2.5 重排序方法:reverse(),sort() 会修改原数组
5.2.6 操作方法

  • .concat():拼接数组
  • .join(分隔符): 把数组中的所有元素转换一个字符串,默认用逗号作为分隔符。不修改原数组
  • .slice(startIndex,endIndex):获取、截取(不包含endIndex项),美[slaɪs]
  • .splice():会修改原数组 【重点】
concat、join、slice 均返回新数组,不改变原数组

5.2.7 位置方法:.indexOf(val,startIndex),返回查找项的位置
5.2.8 数组的迭代方法:【ES5;IE9+等支持】

  • every(),some()
  • filter(),map(); forEach()

5.2.9 归并方法:reduce()——将数组元素计算为一个值(从左到右)

注意:
every() 、some()、filter()、map()、reduce() 都不会对空数组进行检测,且都不会改变原始数组。
map是返回一个新数组,原数组不变;forEach 没有返回值,本质上等同于 for 循环,对每一项执行 function 函数。可能会改变原数组,forEach也不会对空数组进行检测。

分类

1.不改变原数组(返回新值):
转换为字符串(逗号分隔):arr.toString()
转换为字符串:.join(分隔符): 把数组中的所有元素转换一个字符串,默认用逗号作为分隔符
拼接数组:.concat(val)
截取:.slice(startIndex,endIndex) 不包含endIndex位上的值。
读取位置:.indexOf(val,startIndex).lastIndexOf(val,startIndex)
迭代:.every(function)、.some(function)、.filter(function)、.map(function)
归并:.reduce(function,initalValue)—— 将数组元素计算为一个值(从左到右)

2.修改原数组:
重排序:.reverse()反转数组顺序, .sort(function)
操作:.push()、.pop()移除最后一项、.shift()移除首部第一项、.unshift()在首部第一位添加,.splice()
迭代:.forEach(function)


Array 类型介绍

Array 特点:

  1. 数组都是 数据的有序列表
  2. ECMAScript 数组的每一项可以保存任何类型的数据
  3. ECMAScript 数组的大小是可以动态调整的(即可以随着数据的添加自动增长以容纳新增数据)

创建 Array 实例

1. new+构造函数

var colors = new Array();
var colors = new Array("red", "blue", "green");

var colors = new Array(3); // 创建一个包含3项(即length值为3)的数组
var names = new Array("Greg"); // 创建一个包含1项,即字符串"Greg"的数组
分析:
给构造函数传递一个值也可以创建数组,因为如果传递的是数值,则会按照该数值创建包含给定项数的数组;而如果传递的是其他类型的参数,则会创建包含那个值的只有一项的数组。

2. 对象字面量表示法

var colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
var names = [];// 创建一个空数组
var values = [1,2,];// 不要这样!这样会创建一个包含 2 或 3 项的数组
var options = [,,,,,]; // 不要这样!这样会创建一个包含 5 或 6 项的数组

length 属性

数组的项数保存在其 length 属性中,这个属性始终会返回 0 或更大的值。
数组最后一项的索引始终是 length-1
特点:它不是只读的!因此,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项

var colors = ["red", "blue", "green"]; // 创建一个包含3个字符串的数组
colors.length = 2;
alert(colors[2]); //undefined
colors.length = 4;// 新增的每一项都会取得 undefined 值
alert(colors[3]);//undefined
colors[colors.length] = "black";// 在数组末尾添加新项
var arr = [1];
arr.length = 5;
arr.push(5);
console.log(arr); // [1, empty × 4, 5]
console.log(arr[2]); // undefined
数组最多可以包含 4 294 967 295(约42亿)个项,这几乎已经能够满足任何编程需求了

5.2.1 检测数组

1. instanceof

适用于只有一个全局执行环境(不适用于包含多个框架时):
如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的 Array 构造函数。

if (value instanceof Array){ 
    //对数组执行某些操作
}
比如竹子用iframe使用的内外层两个框架,数据通信频繁,所以我当时不会使用 instanceof 来检测是否是数组类型,一般使用 Array.isArray() 或 lodash的isArray(),或Object.prototype.toString.call(value)的方式来实现检测。

2. Array.isArray()

ECMAScript 5 新增,不用管它是在哪个全局执行环境中创建的。支持的浏览器有 IE9+、Firefox 4+、Safari 5+、Opera 10.5+和 Chrome。

if (Array.isArray(value)){ 
    //对数组执行某些操作
}

准确检测

我的文章《判断数组类型的终极大法》

Array 常用方法汇总【ES5】

5.2.2 转换方法

所有对象都具有 toLocaleString()toString()valueOf()方法。
数组的 toString()方法:会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。
调用 valueOf()返回的还是数组本身。

var a = [1,2,3];
var a2 = a.toString();
console.log(a,a2); // [1, 2, 3]   "1,2,3"

[[1,2,[3]],4,true,{name:'cc'}].toString(); // "1,2,3,4,true,[object Object]"
toString() 不修改原数组。

5.2.3 栈方法

栈是一种 LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。
ECMAScript 为数组专门提供了push()pop()方法,以便实现类似栈的行为。

  • push():可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
  • pop():则从数组末尾移除最后一项,减少数组的length值,然后返回移除的项。

5.2.4 队列方法

队列数据结构的访问规则是 FIFO(First-In-First-Out, 先进先出)。
结合使用shift()push()方法,可以像使用队列一样使用数组。

  • shift():它能够移除数组中的第一个项并返回该项,同时将数组长度减1。

反向队列

结合使用unshift()pop()方法,可以从相反的方向来模拟队列(即在数组的前端添加项,从数组末端移除项):

  • unshift()shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。
push()、pop()、shift()、unshift()都会修改原数组。

5.2.5 重排序方法

数组中已经存在两个可以直接用来重排序的方法:reverse()sort(),都会修改原数组。

  • reverse():会反转数组项的顺序。
  • sort():会调用每个数组项的 toString()转型方法,然后比较得到的字符串,以确定如何排序。
// 因为数值 5 虽然小于 10,但在进行字符串比较时,"10"则位于"5"的前面,于是数组的顺序就被修改了。
 var values = [0, 1, 5, 10, 15];
    values.sort();
    alert(values);     //0,1,10,15,5

更好的解决方案:sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。如下:

// 升序
var nums = [1,3,2,10,5].sort(function(num1,num2){
    return num1-num2;
});
console.log(nums);// [1, 2, 3, 5, 10]

5.2.6 操作方法【重点】

1. concat()

concat() 可以拼接任意数组,返回新数组,不改变自身。

var a = [1,2,3];
var b = a.concat(4,5); // b =[1,2,3,4,5]; a = [1,2,3]

var c = a.concat(4,5,[1,2]); // [1, 2, 3, 4, 5, 1, 2]
var d = a.concat(4,5,[1,[2,3]]); // [1, 2, 3, 4, 5, 1, [2,3]]

2. slice()

[slaɪs].

它能够基于当前数组中的一或多个项创建一个新数组(适合截取获得某个数组中的数据)。slice()方法不会影响原始数组。
在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项— —但不包括结束位置的项。

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,4);
alert(colors2);   //green,blue,yellow,purple
alert(colors3);   //green,blue,yellow

3. splice()【重点】(P95)

[splaɪs].

这个方法恐怕要算是最强大的数组方法了!主要用途是向数组的中部插入项。
splice()方法会影响原始数组。

5.2.7 位置方法

ECMAScript 5 为数组实例添加了两个位置方法:indexOf()lastIndexOf()。这两个方法都接收两个参数:要查找的项 和(可选的)表示查找起点位置的索引。在没找到的情况下返回-1。

注意:在比较第一个参数与数组中的每一项时,会使用全等操作符(===,即要求查找的项必须严格相等。
  • indexOf(val,startIndex)方法:从数组的开头(位置0)开始向后查找。
  • lastIndexOf(val,startIndex)方法:则从数组的末尾开始向前查找。
 var numbers = [1,2,3,4,5,4,3,2,1];
 alert(numbers.indexOf(4));        //3
 alert(numbers.indexOf(4, 4));     //5。查找项值为4,从下表4开始查找
    
  var person = { name: "Nicholas" };
  var people = [{ name: "Nicholas" }];
  var morePeople = [person];
  alert(people.indexOf(person));     //-1
  alert(morePeople.indexOf(person)); //0  引用类型

5.2.8 数组的迭代方法【重点!必须会用】(P96)

ECMAScript 5 为数组定义了5个迭代方法。

每个方法都接收两个参数:要在每一项上运行的函数 和(可选的)运行该函数的作用域对象——影响 this 的值。

传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。即:function(val,index,array){}

  • every(): 对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。
  • some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。
  • filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的新数组。适合查询符合某些条件的所有数组项。【不会改变原数组】
  • map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的新数组。适合创建包含的项与另一个数组一一对应的数组。【不会改变原数组】
  • forEach():对数组中的每一项运行给定函数。这个方法没有返回值【本质上等同于 for 循环,对每一项执行 function 函数。可能会改变原数组】

    forEach() 本身不支持 continue 与 break 语句的,使用 return 语句实现 continue 关键字的效果。forEach() 对于空数组是不会执行回调函数的。
注意: every() 、some()、filter()、map() 不会对空数组进行检测,且都不会改变原始数组。

map是返回一个新数组,原数组不变;forEach 是改变原数组。forEach也不会对空数组进行检测。
注意:
使用map时给定函数应该始终返回内容,来使map最终返回一个新数组,而不应该在给定函数内部直接修改原数组!
使用 forEach 时如果传入的是数组对象,如果直接修改对象,则forEach 会修改原数组!

最相似的是 every()和 some(),它们都用于查询数组中的项是否满足某个条件。

注意:
支持这些迭代方法的浏览器有 IE9+、Firefox 2+、Safari 3+、Opera 9.5+和 Chrome。

eg:

const result =[2, 5, 8, 1, 4].some((val, key, array) => {
  return val > 7;
});
console.log(result); // true

5.2.9 归并方法reduce()(【重点】P98)

Array.prototype.reduce()

ECMAScript 5 还新增了两个归并数组的方法:reduce()reduceRight()这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。
传给 reduce()reduceRight()的函数接收4个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项。

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素。

  • reduce():从数组的第一项开始,逐个遍历到最后。
  • reduceRight():则从数组的最后一项开始,向前遍历到第一项。
reduce() 对于空数组是不会执行回调函数的,且不会改变原始数组。
// 语法
arr.reduce(callback,[initialValue])
  • callback (执行数组中每个值的函数,包含四个参数)
  • previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
  • currentValue (数组中当前被处理的元素)
  • index (当前元素在数组中的索引)
  • array (调用 reduce 的数组)
  • initialValue (作为第一次调用 callback 的第一个参数。)

示例

找出数组中的最大值?【典例】

var arr = [10, 0, 100, 99, 48, 101];
const max = arr.reduce((prev, cur) => {
  return Math.max(prev, cur);
}, 0);
console.log(max); // 101

找出年龄最大的人?

    var people = [
      { age: 18, person: "a" },
      { age: 27, person: "b" },
      { age: 33, person: "c" },
      { age: 20, person: "d" },
    ];

    var person = people.reduce(
      (prev, cur) => {
        return cur.age > prev.age ? cur : prev;
      },
      { age: 0 }
    );
    console.log(person); // { age: 33, person: "c" }

函数求和【典例】

function sum() {
  const args = Array.from(arguments);// 把类数组转成数组
  // 或者
  // const args = Array.prototype.slice.call(arguments);
  const sumVal = args.reduce((total, nextVal) => {
    return total + parseFloat(nextVal);
  }, 0);

  return sumVal;
}

// 我的更简易写法
function sum(...rest) {
  return rest.reduce((total, cur) => {
    return total + parseFloat(cur);
  }, 0);
}

sum(1, 2, "3");// 6
sum(1, 2, "3.5");// 6.5

reduce进阶应用

  1. 数组去重,并按从小到大排列
    var arr = [1, 3, 2, 2, 3, 4, 5, 5, 6];
    var result = arr.reduce((prev, cur) => {
      if (!prev.includes(cur)) {
        prev.push(cur);
      }

      return prev;
    }, []);

    result.sort((num1, num2) => {
      return num1 - num2;
    });

    console.log(result); // [1, 2, 3, 4, 5, 6]
  1. 求数组中所有值之和【典例】
var values = [1,2,3,4,5];
var sum = values.reduce(function(total, cur, index, array){
    return total + cur;
},0);
alert(sum); //15

// 若不使用reduce,使用forEach:
var total = 0;
values.forEach((val)=>{total+=val});
分析:
第一次执行回调函数,prev 是 1,cur 是 2。第二次,prev 是 3(1 加 2 的结果),cur 是 3(数组的第三项)。这个过程会持续到把数组中的每一项都访问一遍,最后返回结果。

2.如何知道一串字符串中 每个字母出现的次数?【典例】

    var arrString = 'abcdaabc';

    var result = arrString.split("").reduce((prev, cur) => {
      prev[cur] > 0 ? prev[cur]++ : (prev[cur] = 1);
      return prev;
    }, {});
    console.log(result); // {a: 3, b: 2, c: 2, d: 1}

3.用来进行各种各样的类型转换

由于可以通过第二参数设置叠加结果的类型初始值,因此这个时候reduce就不再仅仅只是做一个加法了,我们可以灵活的运用它来进行各种各样的类型转换,比如将数组按照一定规则转换为对象,也可以将一种形式的数组转换为另一种形式的数组,大家可以动手去尝试一样。

[1, 2].reduce(function(res, cur) { 
    res.push(cur + 1); 
    return res; 
}, []) // [2,3]

4.求多科成绩总和

某同学的期末成绩如下表示

var result = [
    {
        subject: 'math',
        score: 88
    },
    {
        subject: 'chinese',
        score: 95
    },
    {
        subject: 'english',
        score: 80
    }
];

如何求该同学的总成绩?

var sum = result.reduce(function(total, cur) {
    return cur.score + total;
}, 0);

假设该同学因为违纪被处罚在总成绩总扣10分,只需要将初始值设置为-10即可。

var sum = result.reduce(function(prev, cur) {
    return cur.score + prev;
}, -10);

我们来给这个例子增加一点难度。假如该同学的总成绩中,各科所占的比重不同,分别为50%,30%,20%,我们应该如何求出最终的权重结果呢?

解决方案如下:

var dis = {
    math: 0.5,
    chinese: 0.3,
    english: 0.2
}

var sum2 = result.reduce(function(total, cur) {
    return cur.score * dis[cur.subject] + total;
}, 0)

console.log(sum2); // 88.5
更多查看我的文章《最新常用Array数组操作》
更多js内容查看我的文章《js高级程序设计》笔记
《5.6.3 String类型+常用字符串操作》
手册:《JavaScript Array 对象》

添加新评论