-
除了Object,Array就是ES中最常用的了。
-
ES数组跟其他编程语言的数组有很大区别:
- ES数组自然也是一组有序的数据
- ES数组中每一个槽位可以存储任意类型的数据
- ES数组是动态大小的。
-
创建数组的方式
- new操作符 + Array构造函数
let colors = new Array();
let colors = new Array(20); // length = 20 let colors = new Array('red', 'blue', 'green'); // length = 3
- 省略new操作符,结果和上面的结果一致。
let aolors = Array(3);
- 数组字面量array literal。
let colors = []
- 数组字面量表示法不会调用Array构造函数。
- new操作符 + Array构造函数
-
Array构造函数还有2个ES6新增的用于创建数组的静态方法:
- from() : 用于将类数组结构转换为数组实例
- of() :用于将一组参数转换为数组实例。
-
Array.from()第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个length属性和可索引元素的结构。
- 可迭代
- 拥有length属性
- 可索引元素
// 字符串会被拆分为单字符数组
Array.form('matt'); // ['m', 'a', 't', 't']
// 可以使用from()将集合和映射转换为一个新数组
const m = new Map().set(1, 2).set(3, 4);
const 3 = new Set().add(1).add(2).add(3).add(4);
Array.from(m); // [[1,2], [3,4]]
Array.from(s); // [1,2,3,4]
// Array.from对现有数组执行浅拷贝
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1);
a1; // [1,2,3,4]
a1 === a2 // false
a1 == a2 // false
// 可以使用任何可迭代对象
const iter = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
};
Array.from(iter); // [1,2,3,4]
// arguments对象可以被轻松地转换为数组
function getArgsArray() {
console.log(arguments); // [argumnets] {'0': 1, '1': 2, '2': 3, '3': 4}
return Arrays.from(arguments)
}
getArgsArray(1,2,3,4); // [1,2,3,4]
// from()也能转换带有必要属性的自定义对象
const arrayLikeObject = {
0: 1,
1: 2,
2: 3,
3: 4,
length: 4
};
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); // a2 : [1, 4, 9, 16] const a3 = Array.from(a1, function(x) { return x ** this.exponent}, { exponent: 2}); // a3: [1, 4, 9, 16]
-
Array.of()可以把一组参数转换成数组。这个方法用于替代在ES6之前常用的Array.prototype.slice.call(arguments),一种笨拙的将arguments对象转换成数组的写法:
Array.of(1,2,3,4); // [1,2,3,4] Array.of(undefined); // [undefined]
-
数组空位
-
使用数组字面量初始化数组时,可以使用一串逗号来创建空位hole。ES会将逗号之间相应索引位置的值当成空位,ES6规范重新定义了该如何处理这些空位。
-
ES6新增的方法和迭代器与早期ES版本中存在的方法行为不同。ES6新增方法普遍将这些空位当成存在的元素,只不过值为undefined。
const a = Array.from([,,,]); for(const val of a) { alert(val === undefined) ; } // true // true // true for(const [ index, value] of options.entries()) { alert(value); } // 1 // // // // 5
-
-
数组索引
-
要取得或设置数组的值,需要使用中括号并提供相应值得数字索引。
-
数组中元素得数量保存在length属性中,这个属性始终返回0或大于0的值。
-
数组length属性只读且通过修改length属性来在数组尾增删元素。
-
使用length属性可以方便地向数组尾添加元素。
-
数组中最后一个元素的索引始终是length - 1,因此下一个新增槽位的索引就是length。
-
数组最多可以包含4 294 967 295个元素。
-
-
检测数组
- 判断一个对象是不是数组:
- 在只有一个网页(因而只有一个全局作用域)的情况下,使用instanceof操作符就可以了。
if (value instanceof Array) { // 操作数组 }
- 一个全局执行上下文的情况下,可以使用instanceof操作符
- 当涉及到网页里有多个框架时,可能设计不同的执行上下文时,ES提供了Array.isArray()方法
if (Array.isArray(value)) { //操作数组 }
- 判断一个对象是不是数组:
-
迭代器方法
- ES6中,Array得原型上暴露了三个用于检索数组内容得方法:keys()、values()和entries()。
- keys()返回数组索引得迭代器,values()返回数组的迭代器,而entries()返回索引/值对的迭代器。
const a = ["foo", "bar", "baz", "qux"]; // 因为这些方法都返回迭代器,所以可以使用Array.from()将之直接转换成数组实例 const aKeys = Array.from(a.keys()); // [0, 1, 2, 3] const aValues = Array.from(a.values()); // ["foo", "bar", "baz", "qux"] const aEntries = Array.from(a.entries()); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]
- 使用ES6的结构可以非常容易地在循环中拆分键/值对:
const a = ["foo", "bar", "baz", "qux"]; for(const [idx, element] of a.entries()) { alert(idx); alert(element); } // 0 // foo // 1 // bar // 2 // baz // 3 // qux
- 使用时要注意浏览器是否支持这些方法。
-
复制和填充方法
-
ES6新增了两个方法:批量复制方法copyWithin()和填充数组方法fill()。
-
这2个方法和函数签名类似,都需要指定既有数组实例上的一个范围,包含开始索引,不包含结束索引。使用找个方法不会改变数组的大小。
-
fill()方法可以向一个已有的数组中插入全局或部分相同的值。
- 开始索引用于指定开始填充的位置,可选。若不提供结束索引,则一致填充到数组末尾。
const zeroes = [0, 0, 0, 0, 0]; // 用5填充 zeroes.fill(5); // [5, 5, 5, 5, 5]; zeroes.fill(0); // 重置 // 用6填充索引大于等于3的元素 zeroes.fill(6, 3); // [0,0,0,6,6] zeroes.fill(0); // 用7填充索引大于1且小于3的元素 zeroes.fill(7, 1, 3); // [0, 7, 7, 0, 0] zeroes.fill(0); // 用8填充索引大于等于1且小于4的元素 // (-4 + zeroes.length = 1) // (-1 + zeroes.length = 4) zeroes.fill(8, -4, -1); // [0, 8, 8, 8, 0] // ******************************* // fill()静默忽略超出数组边界、零长度及方向相反的索引范围: const zeroes = [0,0,0,0,0]; // 索引过低,忽略 zeroes.fill(1, -10, -6); // [0,0,0,0,0] // 索引过高,忽略 zeroes.fill(1, 10, 15) // 索引反向,忽略 zeroes.fill(2, 4, 2) // 索引部分可用,填充可用部分 zeroes.fill(4, 3, 10); // [0, 0, 0, 4, 4]
- 与fill()不同,copyWithin()会按照指定范围浅拷贝数组中的部分内容,然后将它们插入到指定索引开始的位置。
- 开始索引和结束索引则与fill()使用同样的计算方法:
let ints, reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset(); // 从ints中复制索引0开始的内容,插到索引5开始的位置 // 在源索引或目标索引到达数组边界时停止 ints.copyWithin(5); // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] // 从ints中复制索引5开始的内容,插入到索引0开始的位置 ints.copyWithin(0, 5); // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9] reset(); // 从ints中复制索引0开始到索引3结束的内容 // 插入到索引4开始的位置 ints.copyWithin(4, 0, 3); // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9] reset(); // javascript引擎在插值前会完整复制范围内的值 // 因此复制期间不存在重写的风险 ints.copyWithin(2, 0, 6); // [0, 1, 0, 1, 2, 3, 4, 5, 8, 9] reset(); // 支持负索引值,与fill()相对于数组末尾计算正向索引的过程是一样的 // -4 + 10 = 6 // -7 + 10 = 3 // -3 + 10 = 7 // 所以是从索引3复制到索引7,插到索引6开始 ints.copyWithin(-4, -7, -3); // [0, 1, 2, 3, 4, 5, 3, 4, 5, 6] reset() // copyWithin()静默忽略超出数组边界、零长度及方向相反的索引范围: // 索引过低,忽略 ints.copyWithin(1, -15, -12); // 索引过高,忽略 ints.copyWithin(1, 12, 15); // 索引反向 ints.copyWithin(2, 4, 2); // 索引部分可用,复制、填充可用部分 ints.copyWithin(4, 7, 10); // [0, 1, 2, 3, 7, 8, 9, 7, 8, 9];
-
-
转换方法
- 所有对象都有toLocaleString()、toString()和valueOf()方法。
- valueOf()返回的是数组本身
- toString()返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。
let colors = [ 'red', 'blue', 'green' ]; colors.toString(); // 'red,blue,green' colors.valueOf(); // ['red', 'blue', 'green']
- toLocaleString()方法也可能返回跟toString()和valueOf()相同的结果,但也不一定。
let person1 = { toLocaleString() { return 'Nikolaos'; } toString() { return 'Nicholas'; } } let person2 = { toLocaleString() { return 'Grigorios'; } toString() { return 'Greg } } let person = [person1, person2]; alert(person); // Nicholas,Greg alert(person.toString()); // Nicholas,Greg alert(person.toLocaleString()); // Nikolas,Grigorios
-
alert()期待的是参数是字符串类型,所以遇到不是字符串类型的数据会在后台调用toString()。
-
继承的方法toLocaleString()以及toString()都返回数组值的逗号分隔的字符串。
-
如果想用不同的分隔符,可以使用join()方法。
-
若数组中某一项是null或undefined,则在join()、toLocaleString()、toString()和ValueOf()返回的结果中会以空字符串表示
-
栈方法
-
ES给数组提供了几个方法,让它看起来像另外一种数据结构。
-
数组对象可以像栈一样,也就是一种限制插入和删除项的数据结构。
-
栈: 先进后出
-
数据插入: push()
-
数据删除: pop()
-
push(): 将数据新增到数组末尾。
-
pop(): 将数组末尾项删除
-
-
队列方法
- 先进先出,在队头获取数据,在队尾新增数据。
- 在数组首(队头)获取数据shift()
- 在数组尾(队尾)新增数据push()
-
排序方法
-
反序: reverse(),将数组逆转
-
升序重新排列数组元素sort():最小值在前,最大值在后
-
sort()
- sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序。
let values = [0, 1, 5, 10, 15]; values.sort(); // 0, 1, 10, 15, 5
- sort()会按照这些数值的字符串形式重新排序,因此会出现上述问题。
- 因此,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, 10]; values.sort(compare); // [0, 1, 5, 10, 15]
- 比较函数也可以产生降序效果,只要把返回值交换一下就可以了
function compare(value1, value2) { if (value1 < value2) { return 1; } else if (value1 > value2) { return -1; } else { return 0; } }
- 比较函数也可以简写成箭头函数
let values = [ 0, 1, 5, 10, 15]; values.sort((a,b) => a < b ? 1 : a > b ? -1 : 0) alert(values); // 15,10,5,1,9
-
-
操作方法
-
concat(),它首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组。
- 若传入一个或多个数组,则concat()会把这些数组的每一项都添加到结果数组。
- 若参数不是数组,则直接把它们添加到结果数组末尾。
let colors = ['red', 'green', 'blue']; let colors2 = colors.concat('yellow', ['black', 'brown']); // ['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); // // ["red", "green", "blue", "yellow", ["black", "brown"]] // 强制打平类数组对象 let colors3 = colors.concat(moreColors); // ["red", "green", "blue", "pink", "cyan"]
-
slice()
- slice()用于创建一个包含原有数组中一个或多个元素的新数组。
- slice()方法可以接收一个或2个参数:返回元素的开始索引和结束索引。不包含结束索引对应的元素。该操作不影响原始数组。
let colors = ['red', 'green', 'blue', 'yellow', 'purple']; let colors2 = colors.slice(1); // ['green', 'blue', 'yellow', 'purple'] let colors3 = colors.slice(1, 4); // ['green', 'blue', 'yellow']
- 如果slice()的参数有负值,那么就以数值长度+该负值的结果确定位置
- length=5;slice(-2, -1)相当于slice(3,4)
-
splice()
- 主要作用就是在数组中间插入元素,有3种不同的方式使用splice()方法:
- 删除。需要给splice()传入2个参数:要删除的第一个元素的位置和要删除数量。
- 插入。需要给splice()传入3个参数:开始位置、0(要删除的元素数量)、要插入的元素,可以在数组中指定位置插入元素。第三个参数之后还可以传第4个、第5个,乃至任意多个要插入的元素。
- 例如: splice(2, 0, ‘red’, ‘green’)
- 替换。splice()在删除元素的同时可以在指定位置插入新元素,同样要传入3个参数:开始位置、要删除元素的数量和要插入的任意多个元素。要插入的元素数量不一定跟删除的元素数量一致。
- 例如:splice(2, 1, ‘red’, ‘green’)
- splice()方法始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)。
let colors = ['red', 'green', 'blue']; let removed = colors.splice(0,1); // 删除第一项 alert(colors); // green,blue alert(removed); // red只有一个元素的数组 removed = colors.splice(1, 0, 'yellow', 'orange'); alert(colors); // green,yellow,orange,blue alert(remived); // 空数组 removed = colors.splice(1, 1, 'red', 'purple'); alert(colors); // green,red,purple,orange,blue alert(remived); // yellow
-
-
搜索和位置方法
-
按严格相等搜索
- indexOf()
- lastIndexOf()
- includes():ES7新增
- 这些方法都接收两个参数:要查找的元素和一个可选的起始搜索位置。indexOf()和includes()方法从数组前头(第一项)开始向后搜索,lastIndexOf()从数组末尾(最后一项)开始向前搜索。
- indexOf()和lastIndexOf()都返回要查找的元素在数组中的位置,如果没有找到返回-1。
- includes()返回布尔值,表示是否至少找到一个与指定元素匹配的项。
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(number.indexOf(4, 4)); // 5 alert(numbers.lastIndexOf(4, 4)); // 3 alert(numbers.includes(4, 7)); // false,从第7位开始向后搜索 let person = { name: 'Nike'}; let people = [{name: 'addree'}]; let morePeople = [person]; alert(people.indexOf(person)); // -1 alert(morePeople.indexOf(person)); // 0 alert(people.includes(person)); // false alert(morePeople.includes(person)); // true
-
按断言函数搜索
- ES允许按照定义的断言函数搜索数组,每个索引都会调用这个函数。断言函数的返回值决定了相应索引是否被认为匹配。
- 断言函数接收3个参数:元素、索引、数组本身;断言函数返回真值,表示是否匹配。
- find()
- findIndex()
- 这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引。这两个方法都接收第二个可选的参数,用于指定断言函数内部的this。
const people = [ { name: 'Nike', age: 27 }, { name: 'kapai', age: 29 } ]; alert(people.find((element, index, array) => element.age < 28)); // {name: 'Nike', age: 27} alert(people.findIndex((element, index, array) => element.age < 28)); // 0
- 找到匹配项后,这两个方法就不再继续搜索
const evens = [2, 4, 6]; // 找到匹配后,永远不会检查数组的最后一个元素 evens.find((element, index, array) => { console.log(element); console.log(index); console.log(array); return element === 4 }) // 2 element // 0 index // [2, 4, 6] array // 4 element // 1 index // [2, 4, 6] array
-
-
迭代方法
- ES为数组定义了5个迭代方法。每个方法接收2个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this的值)。
- 函数接收三个参数:数组元素,元素索引和数组本身。
- every():对数组每一项都运行传入的函数,如果对每一项函数都返回true,则这个方法返回true
- filter():对数组每一项都运行传入的函数,函数返回true得项会组成数组之后返回
- forEach():对数组每一项都运行传入的函数,没有返回值
- map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
- some():对数组每一项都运行传入的函数,如果有一项函数返回true,则这个方法返回true。
- 这些函数都不改变调用它们的数组。
- every()和some()类似,但返回true的条件不一样。
let numbers = [1,2,3,4,5,4,3,2,1]; let everyResult = numbers.every((item, index, array) => item > 2); // false let someResult = numbers.some((item, index, array) => item > 2); // true
- filter()方法。这个方法基于给定的函数来决定某一项是否应该包含在它返回的数组中。
let numbers = [1,2,3,4,5,4,3,2,1]; let filterResult = numbers.filter((item, index, array) => item > 2); // [3,4,5,4,3]
- map()方法返回一个数组。这个数组的每一项都是对原始数组中同样位置的元素运行传入函数而得到的结果。
let mapResult = numbers.map((item, index, array) => item * 2); // [2,4,6,8,10, 8,6,4,2]
- forEcah()方法只会对每一项运行传入的函数,没有返回值。相当于for循环
-
归并方法
- reduce():从数组第一项开始遍历到最后一项。
- reduceRight():从最后一项开始遍历至第一项。
- 这两个方法都会迭代数组的的所有项,并在此基础上构建一个最终返回值。
- 这两个方法都接收2ge参数:对每一项都会运行的归并函数,以及可选的以之为归并起点的初始值。
- 传给reduce()和reduceRight()的函数接收4ge参数:上一个归并值、当前项、当前项的索引和数组本身。
- 这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。
let values = [1, 2, 3, 4, 5]; let sum = values.reduce((prev, cur, index, array) => prev + cur); // 15 let sum2 = values.reduceRight(function(prev, cur, index, array) { return prev + cur }); // 15