JavaScript 引用类型五(集合引用类型Array)

112 阅读23分钟

概述

ECMAScript 数组跟其他编程语言的数组有很大区别。跟其他语言中的数组一样,ECMAScript 数组也是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任一类型的数据。这意味着可以创建一个数组,它的第一个元素是字符串,第二个元素是数值,第三个是对象。ECMAScript 数组也是动态大小的,会随着数据添而自动增长

创建数组的几种方式

通过Array 构造函数创建数组

let colors = new Array();

如果知道数组中元素的数量,那么可以给构造函数传入一个数值,然后length 属性就会被自动创建为这个值。

let colors = new Array(20);

也可以给Array 构造函数传入要保存的元素。

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

创建数组时可以给构造函数传入一个值。这个时候会有些分歧,具体逻辑如下,如果这个值时数值,则会创建一个长度为指定数值的数组;而如果这个值是其他类型的,则会闯进啊一个值包含该特定值的数组。

let colors = new Array(3); //创建一个包含3个元素的数组
let names = new Array("Greg");// 创建一个值包含一个元素,即字符串"Greg"的数组、

在使用构造函数时,也可以省略new 操作符。

let colors = Array(3);//创建一个包含3个元素的数组
let names = Array("Greg");// 创建一个值包含一个元素,即字符串"Greg"的数组、

使用数组字面量表示法创建数组

数组字面量是在中括号中包含以逗号分隔的元素列表。

let colors = ["red","blue","green"]; // 创建一个包含3个元素的数组
let names = []; // 创建一个空数组
let values = [1,2]; // 创建一个包含2个元素的数组

与对象一样,在使用数组字面量表示法创建数组不会调用Array构造函数。

ES6 新增的创建数组的方法,from() 和 of()

Array 构造函数还有两个ES6 新增的用于创建数组的静态方法:from()和of().from() 用于将类数组结构转换为数组实例,而of()用于将一组参数转换为数组实例。

Array.from()

Array.from()的第一个参数时一个类数组对象,即任何可迭代的结构,或者有一个length属性和可索引元素的结构。 Array.from()执行的是浅复制,如果存在于数组中的元素为原始值类型,则相当于复制了一份,对Array.from()得到的数组中的元素进行修改,不会影响到原始数组。然如果数组中存放的是引用类型,则修改会影响到原始数组中的至值。

// 字符串会被拆分为单字符数组
console.log(Array.from("Matt")); // ["M","a","t","t"]

//可以使用from()将集合和映射转换为一个新数组
const m = new Map().set(1,2),
                   .set(3,4);
const s = new Set().add(1)
                   .add(2)
                   .add(3)
                   .add(4);
console.log(Array.from(m));// [[1,2],[3,4]]
console.log(Array.from(s));//[1,2,3,4]

//Array.from()对现有数组执行浅复制
const a1 = [1,2,3,4];
cosnt a2 = Array.from(a1);

console.log(a1); //[1,2,3,4]
alert(a1 === a2);//false

let originalArray = [
        { name: "aaa", age: 26 },
        {
          name: "bbb",
          age: 27,
        },
      ];
 let b = originalArray;
alert(originalArray === b);//true

// 可以使用任何可迭代对象
const iter = {
    *[Symbol.iterator] () {
        yield 1;
        yield 2;
        yield 3;
        yield 4;
    }
};
console.log(Array.from(iter)); // [1,2,3,4]

//arguments 对象可以被轻松的转换为数组
function getArgsArray() {
    reutrun Array.from(arguments);
}
console.log(getArgsArray(1,2,3,4));// [1,2,3,4]

//from()也能转换带有必要属性的自定义对象
const arrayLikeObject = {
    0:1,
    1:2,
    2:3,
    3:4,
    length:4
};
console.log(Array.from(arrayLikeObject));// [1,2,3,4]

Array.from() 还接收第二个可选的映射函数参数。这个函数可以直接增强新数组的值,而无需像调用Array.from().map()那样先创建一个中间数组。还可以接收第三个可选的参数,用于映射函数中this的值。单这个重写的this值在箭头函数中不适用

const a1 = [1,2,3,4];
const a2 = Array.from(a1,x => x ** 2);
const a3 = Array.from(a1,function(x) {return x ** this.exponent}, {exponent:2});
console.log(a2);// [1,4,9,16]
console.log(a3);// [1,4,,9,16]

Array.of()

Array.of() 可以把一组参数转换为数组。这个方法用于替代ES6 之前常用的Array.prototype.slice.call(arguments),一种异常笨拙的将arguments 对象转换为数组的写法:

console.log(Array.of(1,2,3,4));// [1,2,3,4]
console.log(Array.of(undefiend));// [undefined]

数组空位

使用数组字面量初始化数组时,可以使用一串逗号来创建空位。ECMAScript会将逗号之间相应索引位置的值当成空位,ES6规范重新定义了该如何处理这些空位。
可以像下面这样创建一个空位数组:

const options = [,,,,,,]; // 创建包含5个元素的数组
console.log(options.length);// 5
console.log(options); // [,,,,,]

ES6 新增的方法和迭代器与早期ECMAScript 版本中存在的方法 行为不同。ES6新增方法普遍讲这些空位当成存在的元素,只不过值为undefined。

const options = [1,,,,5];
for(const opiton of options){
    console.log(option === undefined);
}
//false 
//true
//true
//true
//false

const a = Array.from ([,,,,]);//  使用ES6 的Array.from()创建的包含3个空位的数组
for(const val of a){
    alert(val === undefined);
}
//true
//true
//true

alert(Array.of(...[,,,,]));//[undefined,undefined,undefined]

for(const [index, value] of options.entires()){
    alert(value);
}
//1
//undefined
//undefined
//undefined
//5

ES6 之前的方法则会忽略这个空位,但具体的行为也会因方法而异。
//map()会跳过空位置
console.log(options.map(() => 6)); // [6,undefined,uhdefined,undefined,6]

//join() 视空位值为空字符串
console.log(opitons.join('-'));// "1----5"

由于行为不一致和存在性能隐患,因此实践中要避免使用数组空位。如果确实需要空位,则可以显示的使用undefined值代替。

数组索引

要取得或设置数组的值,需要使用中括号并提供相应值的数字索引。

let colors = ["red","blue","green"];
alert(colors[0]); //显示第一项
colors[2] = "black"; // 修改第三项
colors[3] = "brown"; // 添加第四项

在中括号中提供的索引表示要访问的值。 如果索引小于数组包含的元素数,则返回存储在相应位置的元素,就像示例中colors[0]显示“red”一样。设置数组的值方法也是一样的,就是替换指定位置的值。如果把一个值设置给超过数组最大索引的索引,就像示例中的colors[3],则数组长度会自动扩展到该索引值加1(示例中设置的索引3,所以数组长度变成了4.)。

数组中元素的数量保存在length 属性中,这个属性始终返回0或大于0的值。

let colors = ["red","blue","green"];//创建一个包含3个字符串的数组
let names = [];

alert(colors.length);//3
alert(names.length);//0

数组length属性的独特之处在于,他不是只读的。通过修改length 属性,可以从数组末尾删除或添加元素。

let colors = ["red","blue","green"]; // 创建一个包含3个元素的数组
colors.length = 2;
alert(colors[2]);// //undefined

这里数组colors 一开始有3个值。将length 设置为2,就删除了最后一个(位置为2的)值。因此colors[2]就没有值了。如果将length 设置为大于数组元素的值,则新加的元素都将以undefined 填充。

let colors = ["red","blue","green"];// 创建一个包含3个字符串的数组
colors.length = 4;
alert(colors[3]);// undefined

使用length 属性可以方便的想数组末尾添加元素。

let colors = ["red","blue","green"];// 创建一个包含3个元素对的数组
colors[colors.length] = "blank";// 添加一种颜色(位置3)
colors[colors.length] = "brown";// 再添加一种颜色(位置4)

数组中最后一个元素的索引始终是length -1,因此下一个新增草味的索引就是length。每次在数组最后一个元素后面新增一项,数组的length 属性都会自动更新,以反映变化。这意味着第二行的colors[colors.length]会在位置3添加一个新元素,下一行则会在位置4添加一个新元素。新的长度会在新增元素被添加到当前数组外部的位置上时自动更新。换句话说,就是length 属性会更新为位置加上1.。

let colors = ["red","blue","green"];//创建一个包含3个字符串的数组
colors[99] = "black";// 添加一种颜色(位置 99)
alert(colors.length); // 100

数组的检测 isArray()

一个经典的ECMAScript 问题是判断一个对象是不是数组。在只有一个网页的情况下,使用instanceof 操作符足矣/。

if(value instanceof Array){
    //操作数组
}

使用instanceof 的问题是假定只有一个全局执行上下文。如果网页里有多个框架,则可能 涉及两个不同的全局执行上下文,因此就会有不同版本的Array 构造函数。如果把数组从一个框架给另一个框架,则这数组的构造函数将有别于在第二个矿机内本地创建的数组。
为解决这个问题,ECMAScript 提供了Array.isArray() 方法。这个方法的目的就是确定一个值是否为数组,而不用管它是在哪个全局执行上下文中创建的。

if(Array.isArray(value)){

}

数组中的迭代器方法 of

在ES6 中,Array 的原型上暴露了3个用于检索数组内容的方法:keys(),values()和entires().keys()返回数组索引的迭代器,vlaues返回数组元素的迭代器,而entires()返回索引/值对的迭代器.

const a = ["foo","bar","baz","qux"];

for(const [idx,element] of a.entires()){
    alert(idx);
    alert(element);
}
//0
//foo
//1
//bar
//2
//baz
//3
//qux

复制和填充方法 fill() & copyWithin()

ES6 新增了两个方法,批量复制方法copyWithin(),以及填充数组方法fill().这两个方法的函数签名类似,都需要指定既有数组实例上的一个范围,包含开始索引,不包含结束索引使用这个方法不会改变数组的大小。

fill()

使用fill()方法可以向一个已有的数组插入全部或部分相同的值。开始所用用于指定开始填充的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。负数索引从数组末尾开始计算。也可以将负索引想象成数组长度加上它得到的一个正索引。

fill()函数接收3个参数,第一个参数,代表要填充的数据,第二个参数表示填充开始的索引,第三个参数表示填充结束的索引(填充时不会填充这个结束索引表示的位置。)

const zeroes = [0,0,0,0,0];

//用5 填充整个数组
zeroes.fill(5);
console.log(zeroes);// [5,5,5,5,5]
zeroes.fill(0); // 重置

//用6 填充索引大于等于3的索引
zeroes.fill(6,3);
console.log(zeroes);// [0,0,0,6,6]
zeroes.fill(0);// 重置

// 用7填充索引大于等于1 且小于3的索引
zeroes.fill(7,1,3);
console.log(zeroes); // [0,7,7,0,0]
zeroes.fill(0);

//用8填充 索引大于等于且小于4的元素
// (-4 + zeroes.length = 1)
// (-1 + zeroes.length = 4)
zeroes.fill(8,-4,-1);
console.log(zeroes);// [0,8,8,8,0]

fill()静默忽略超出数组边界,零长度及方向相反的索引范围:

const zeroes = [0,0,0,0,0];

//索引过低,忽略
zeroes.fill(1,-10,-6);
console.log(zeroes);//[0,0,0,0,0]

//索引过高,忽略
zeroes.fill(1,10,15);
console.log(zeroes);//[0,0,0,0,0]

//索引反向,忽略
zeroes.fill(1,4,2);
console.log(zeroes);//[0,0,0,0,0]

//索引部分可用,填充可用部分
zeroes.fill(1,3,10);
console.log(zeroes);//[0,0,0,4,4]

copyWithin()

与fill() 不同,copyWithin() 会按照指定范围浅复制数组中的部分内容,然后将他们插入到指定索引开始的位置,开始索引与结束索引则与fill()使用相同的计算方法:

copyWithin()接受三个参数,第一个参数表示要插入的索引位置,第二个参数和第三个参数组合在一起表示复制数据的范围,并将复制范围内的数据插入到第一个参数表示的插入位置。

let ints,
    reset = () => ints = [0,1,2,3,4,5,6,7,8,9];
reset();

//从ints 中复制索引0开始的内容,插入到索引5开始的位置
ints.copyWithin(5);
console.log(ints);//[0,1,2,3,4,0,1,2,3,4]
reset();

//从ints 中复制索引5 开始的内容,插入到索引0 开始的位置
ints.copyWithin(0,5);
console.log(ints);// [5,6,7,8,9,5,6,7,8,9]
reset();

// 从ints 中复制索引0开始到索引3结束的内容
// 插入到索引4开始的位置
ints.copyWithin(4,0,3);
console.log(ints);// [0,1,2,3,0,1,2,7,8,9]
reset();

//JavaScript 引擎在插值前会完整复制范围内的值
//因此复制期间不存在重写的风险
ints.copyWithin(2,0,6);
alert(ints); // [0,1,0,1,2,3,4,5,8,9]
reset();

//支持负索引值,与fill()相对于数组末尾计算正向索引的过程是一样的
ints.copyWithin(-4,-7,-3);
alert(ints);//[0,1,2,3,4,5,3,4,5,6]

copyWithin() 静默忽略超出数组边界,零长度及方向相反的索引范围

let ints,
    reset = () => ints = [0,1,2,3,4,5,6,7,8,9];
reset();

//索引过低,忽略
ints.copyWithin(1,-15,-12);
alert(ints);//[0,1,2,3,4,5,6,7,8,9]
reset();

//索引过高,忽略
ints.copyWithin(1,12,15);
alert(ints);//[0,1,2,3,4,5,6,7,8,9]
reset();

//索引反向,忽略
ints.copyWithin(1,4,2);
alert(ints);//[0,1,2,3,4,5,6,7,8,9]
reset();

//索引部分可用,复制,填充可用部分
ints.copyWithin(1,4,2);
alert(ints);//[0,1,2,3,7,8,9,7,8,9]
reset();

数组转字符串方法 toLocaleString(),toString()和valueOf()

所有对象都有toLocaleString(),toString()和valueOf()方法。 其中,valueOf()返回的还是数组本身。
而toString()返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。也就是说,对数组的每个值都会调用其toString()方法,以得到最终的字符串。

let colors = ["red","blue","green"]; // 创建一个包含3个字符串的数组
console.log(colors.toString());// red,blue,green
console.log(colors.valueOf());['red','blue','green']
console.log(colors);// ['red','blue','green']
alert(colors.valueOf()); red,blue,green  // alert 期望得到一个字符串,所以会在后台调用数组的toString()方法
alert(colors);//  red,blue,green]

toLocaleString()方法也可能返回跟toString()和valueOf()相同的结果,但也不一定。
在调用数组的toLocaleString()方法时,会得到一个逗号分隔的数组值的字符串。他与另外两个方法唯一的区别是,为了得到最终的字符串,会调用数组每个值的toLocaleString()方法,而不是toString()方法。

let person1 = {
    toLocaleString() {
        return "Nikolaos";
    }
    toString() {
        return "Nicholas";
    }
};

let person2 = {
    toLocaleString() {
        return "Grigoios";
    }
    
    toString() {
        return "Greg";
    }
}

let people = [person1,person2];
alert(people);//Nocholas,Greg
alert(people.toString()); // Nicholas,Greg
alert(people.toLocaleString());//Nikolaos,Grigorios

继承的方法toLocalString() 以及toString()都返回数组的逗号分隔的字符串。如果想使用不同的分隔符,则可以使用join()方法。join()方法接收一个参数,即字符串分隔符,返回包含所有项的字符串。

let colors = ["red","green","blue"];
alert(colors.join(","));// red,green,blue
alert(colors.join("||"));//red||green||blue

如果数组中某一项是null 或undefined,则在join(),toLocaleString(),toString()和valueOf()返回的结果中会以空字符串表示。

栈方法 push() & pop()

ECMASCript 给数组提供几个方法,让他看起来像是另外一种数据结构。数组对象可以像栈一样,也就是一种限制插入和删除项的数据结构。栈是一种后进先出的结构,也就是最近添加的项先被删除。数据的插入(称为推入) 和删除(称为弹出)只在栈的的一个地方发生,即栈顶。ECMAScript 数组提供了push()和pop()方法,以实现类似栈的行为。 push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度。pop()方法则用于删除数组的最后一项,同时减少数组的length值,返回被删除的项。

let colors = new Array();
let count = colors.push("red","green");// 推入两项
alert(count);//2

count = colors.push("black"); // 再推入一项
alert(count);// 3

let item = colors.pop();//取得最后一项
alert(item); // black
alert(colors.length);// 2

栈方法可以同数组的其他方法一起使用。

队列方法 shift()&push(),unshift()& pop()

就像栈是以LIFO形式限制访问的数据结构一样,队列以先进先出(FIFO)的形式限制访问。队列在列表末尾添加数据,但从表头获取数据。因为又了再数据末尾添加数据的push()方法,所以要模拟队列就差一个从头组头开始取得数据的方法了。这个数组方法叫shift(),它会删除数组的第一项并返回它,然后数组长度减1.使用shift()和push(),可以把数组当成队列来使用。

let colors = new Array();
let count = colors.push("red","green");//推入两项
alert(count);//2

count = colors.push("black");// 再推入一项
alert(count);//3


let item = colors.shift();// 取得第一项
alert(item);//red
alert(colors.length);//2

ECMAScript 也为数组提供了unshift()方法。顾名思义, unshift()就是执行跟shift()相反的操作,在数据开头添加任意多个值,然后返回新的数组长度。通过使用unshift()和pop(),可以在相反方向上模拟队列,即在数组开头添加数据,在数组末尾取得数据。

let colors = new Array();
let count = colors.unshift("red","green");//从数组开头推入两项
alert(count);// 2

count = colors.unshift("black");// 再推入一项
alert(count);

let item = colors.pop();//取得最后一项
alert(item);// green
alert(colors.length);// 2

排序方法 reverse(),sort()

数组有两个方法可以用来对元素重新排序:reverse()和sort().
顾名思义,reverse()方法就是将数组元素反向排列。

let values = [1,2,3,4,5];
values.reverse();
alert(values);// 5,4,3,2,1

reverse()方法很直观,但不够灵活,所以有了sort()方法。
默认情况下,sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。为此sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。即使数组的元素都是数值,也会把数组内的元素转换为字符串再比较,排序。

let values = [0,1,5,10,15];
values.sort();
console.log(values);// 0,1,10,15,5

一开始数组中值的顺序是正确的,但调用sort()会按照这些数组的字符串形式重新排序。因此,即使5 小于10,但字符串“10”在字符串“5”的前头,所以10还是会排到5前面。很明显,这在多数情况下都不是最适合的。
为此,sort()方法可以接收一个比较函数,用于判断哪个值应该排在前面。比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相等,就返回0;如果第一个参数应该排在第二个参数后面,就返回正值。

function compare(value1,value2) {
    if(value1 < value2){
        return -1;
    }else if(value1 > value2){
        return 1;
    }else {
    return 0;
    }
}

let values = [0,1,5,15];
values.sort(compare);
alert(values);// 0,1,5,10,15

此外,这个函数还可以简写为一个箭头函数

let values = [0,1,5,10,15];
values.sort((a,b) => a < b ? -1 : a > b ? 1 : 0);
alert(values); // 0,1,5,10 ,15

reverse() 和 sort() 都返回调用它们的数组的引用。
如果数组的元素是数值,或者是其valueOf()方法返回数值的对象(如Date对象),这个比较函数还可以写的更简单,因为这时可以直接用第二个值减去第一个值

function compare(value1,value2){
    return value2 - value1;
}

数组操作方法:concat(),slice(),splice()

concat()

concat() 方法可以在现有数组全部元素基础上创建一个新数组。
他首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组
如果传入一个或多个数组,则concat()会把这些数组的每一项都添加到结果数组。如果参数不是数组,则直接将它们添加到结果数组末尾。

let colors = ["red","green","blue"];
let colors2 = colors.concat("yellow",["black","brown"]);

console.log(colors);// ["red","green","blue"]
console.log(colors2);// ["red","green","blue","black","brown"]

打平数组参数的行为可以重写,方法是在数组上指定一个特殊的符号:Symbol.isConcatSpreadable.这个符号能够阻止concat()打平参数数组。相反,把这个值设置为true 可以强制打平类数组对象。

let colors = ["red","green","blue"];
let newColors = ["black","brown"];
let moreNewColors = {
    [Symbol.isConcatSpreadable]:true,
    length :2,
    0:"pink",
    1:"cyan"
};

newColors[Symbol.isConcatSpreadable] = false;

//强制不打平数组
let colors2 = colors.concat("yellow",newColors);

//强制打平数组
colors3 = colors.concat(moreNewColors);

console.log(colors);//["red","green","blue"]
console.log(colors2);//["red","green","blue",["black",cyan]]
console.log(colros3);//["red","green","blue","black","brown"]

slice()

方法slice() 用于创建一个包含原有数组中一个或多个元素的新数组。

slice()方法可以接收一个或两个参数:返回元素的开始索引和结束索引。

如果只有一个参数,则slice()会返回该索引到数组末尾的所有元素。

如果有两个参数,则slice()返回从索引开始到结束索引对应的所有元素,其中不包含结束索引对应的元素。记住,这个操作不影响原始数组。

如果slice() 函数有负值,那么就以数组长度加上这个负值的结果确定位置。如果结束位置小于开始位置,则返回空数组。

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

alert(colors2);// green,blue,yellow,purple
alert(colors3);//green,blue,yellow

splice()

splice()的主要目的是在数组中间插入数组,但有3种不同的方式使用这个数组。

删除。 需要给splice()传入2个参数:要删除的第一个元素的位置和要删除的元素的数量。可以从数组中删除任意多个元素,比如splice(0,2)会删除前两个元素。

插入。 需要给splice()传入3个参数:开始位置,0(要删除的元素数量),和要插入的元素,可以在数组中指定的位置插入元素。第三个参数之后还可以出传入第四个,第五个参数,乃至任意多个要插入的元素。比如,splice(2,0,"red","green")会从数组位置2开始插入字符串"red"和"green";

替换。 splice()在删除元素的同时,可以在指定位置插入新元素,同样需要传入3个参数:开始位置,要删除元素的数量和要插入的任意多个元素。要插入的元素数量不一定跟删除的元素的数量一致。比如,splice(2,1,"red","green")会在位置2删除一个元素,然后从该位置开始向数组中插入"red"和"green"。

splice() 方法始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)。

let colors = ["red","green","blue"];
let removed = colors.splice(0,1);// 删除第一项
alert(colors);//green,blue

removed = colors.splice(1,0,"yellow","orange");//在位置1 插入两个元素
alert(colors); //green,yellow,orange,blue
alert(removed);// 空数组

removed = colors.splice(1,1,"red","purple");// 插入两个值,删除一个元素
alert(colors);//green,red,purple,orange,blue
alert(removed);// yellow

搜索和位置方法

按严格搜索:indexOf(),lastIndexOf()和includes().

ECMAScript 提供了3个 严格相等的搜索方法:indexOf,lastIndexOf(),includes().其中前两个方法在所有版本中都可用,而第三个方法是ECMAScript 7 新增的。\

这三个方法都接收两个参数:要查找的元素 和一个可选的起始搜索位置。

indexOf()和includes()方法从数组前头(第一项)开始向后搜索,而lastIndexOf()从数组末尾(最后一项)开始向前搜索。

indexOf() 和lastIndexOf()都返回要查找的元素在数组中的位置,如果没有找到则返回-1。

incluedes()返回布尔值。表示是否至少找到一个与指定元素匹配的项。

在比较第一个参数跟数组每一项时,会使用(===)比较,也就是说两项必须严格相等。

let numbers = [1,2,3,4,5,4,3,2,1];

alert(numbers.indexOf(4));// 3
alert(numbers.lastIndexOf(4));//5
alert(numbers.includes(4));//true


alert(numbers.indexOf(4,4));// 5
alert(numbers.lastIndexOf(4,4));//3
alert(numbers.includes(4,7));//false

let person = {name: "Nicholas"};
let people = [{name: "Nicholas"}];
let morePeople = [person];

alert(people.indexOf(person));// -1
alert(morePeople.indexOf(person));// 0
alert(people.includes(persoon));//false
alert(morePeople.includes(person));//true

断言函数搜索 find(),findIndes()

ECMAScript 也允许按照定义的断言函数搜索数组,每个索引都会调用这个函数。断言函数的返回值决定了相应索引的元素是否被认为匹配。

断言函数接收3个参数:元素,索引和数组本身。其中元素时数组中当前搜索的元素,索引是当前元素的索引,而数组就是正在搜索的数组。断言函数返回真值,表示是否匹配。

find()和findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引。这两个方法也都接收第二个可选的参数,用于指定断言函数内部this的值。 找到匹配项后,这两个方法都不再继续查找。

const people = [ {
        name:"Matt",
        age:27
    },
    {
        name:"Nicholas",
        age:29
    }
];


alert(people.find((element,index,array) => element.age < 28));
//{name: "Matt",age:27}

alert(people.findIndex((element,index,array) => element.age < 28));
//0

迭代方法 every(),filter(),forEach(),map(),some()

ECMAScript 为数组定义了5个迭代方法。每个方法接收两个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this的值 )。传给每个方法的函数接收3个参数:数组元素,元素索引和数组本身。因具体方法而异,这个函数的执行结果可能会也可能不会影响方法的返回值。

every():对数组每一项都运行传入的函数,如果对每一项都返回true,则这个方法返回true。

filter():对数组每一项都运行传入的函数,函数返回true的项会组成数组之后返回,

forEach():对数组每一项都运行传入的函数,没有返回值。

map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。

some():对数组的每一项都运行传入的函数,如果有一项函数返回true,则这个方法返回true。

在这些方法中,every()和some()是最相似的,都是从数组中搜索符合某个条件的元素。对every()来说传入的函数必须对每一项都返回true,它才会返回true。否则,它就返回false。而对some()来说,只要有一项让传入的函数返回true,它就会返回true。

let numbers = [1,2,3,4,5,4,3,2,1];

let everyResult = numbers.every((item,index,array) => item >2);
alert(everyResult);// false

let someResult = numbers.some((item,index,array) => item >2 );
alert(someResult);//true

filter()方法 基于给定的函数来决定某一项是否应该包含在它返回的数组中。比如要返回一个所有数值都大于2的数组:

let numbers = [1,2,3,4,5,4,3,2,1];

let filterResult = numbers.filter((item,index,array) => item >2);
alert(filterResult); // 3,4,5,4,3

map()方法也会返回一个数组。这个数组的每一项都是对原始数组中同样位置的元素运行传入的函数而返回的结果。

let numbers = [1,2,3,4,5,4,3,2,1]; 

let mapResult = numbers.map((item,index,array) => item * 2);

alert(mapResult); // 2,4,6,8,10,8,6,4,2

forEach()方法只会对每一项运行传入的函数,没有返回值。

let numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach((item,index,array) = >{

});

归并方法 reduce() & reduceRight()

ECMAScript 为数组提供了两个归并方法:reduce() 和 reduceRight().这两个方法都会迭代数组的所有项,并在此基础上构建一个最终的返回值。reduce()方法从数组的第一项开始遍历到最后一项,而reduceRight()从最后一项开始遍历到第一项。

这两个方法都接收两个参数,对每项都会运行的函数,以及可选的以之为归并起点的初始值。传给reduce()和reduceRight()的函数接收4个参数:上一个归并值,当前项,当前项的索引和数组本身。这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。如果没有给这两个方法传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数的第一个参数的是数组的第一项,第二个参数是数组的第二项。

可以使用reduce()函数执行累加数组中所有数值的操作。

let values = [1,2,3,4,5];
let sum = values.reduce(prev,cur,index,array) => prev + cur);

alert(sum);//15

reduceRight() 方法与之类似,只是方向相反。

let values = [1,2,3,4,5];
let sum = values.reduceRight(function(prev,cur,index,array){
    return  prev + cur;
});
console.log(sum);// 15