Array类型
与其它语言不同,ECMAScript数组的每一项可以保存任何类型的数据。
而且,ECMAScript数组的大小是动态调整的。
创建数组
创建数组有两种方式:
- Array构造函数
var colors = new Array();
如果预先知道要保存的数据的数量,也可以给构造函数传递该数量,该数量也会变成length属性的值。
var colors = new Array(20);
也可以向Array构造函数传递数组项:
var colors = new Array("red","blue");
给构造函数传一个值也可以创建数组: 传递的值为数值则创建该长度的数组,传递的值为其他类型则创建只包含该值一项的数组。
在使用Array构造函数创建数组时也可以省略new操作符。
- 使用数组字面量表示法:
var colors = ["red","blue"];
var names = [];
var names = [1,2,]; //这样会创建包含2或3项的数组
var options = [,,,,,]; //这样会创建包含5或6项的数组
和object一样,在使用数组字面量表示法时也不会调用Array构造函数。
数组属性
通过方括号和下标访问数组项,如果索引超过了数组现有项数,数组就会自动增加到该索引值加1的长度。
数组的length属性不只是只读的。通过设置length可以从数组的末尾移除项或者向数组中添加新项。(新增的项都是undefined)。
利用length属性可以方便地在数组末尾添加新项:
colors[colors.length] = "black";
是因为数组最后一项的索引是length-1,下一个新项的位置就是length。
var colors = ["red","blue"];
colors[99] = "black";
colors.length;//100,位置2到位置98都是undefined
转换成字符串
- toString方法:会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。
- 会调用数组每一项的toString方法
- alert一个数组,不管用toString,valueOf还是直接alert数组,都会显示和toString相同的结果,因为alert要接收字符串参数,会自动在后台调用toString方法。
- valueOf方法:返回的还是数组
- toLocaleString方法
- 会调用每一项的toLocaleString方法
join()
上述的三种方法都会返回以逗号分隔的字符串形式,而join方法可以指定分隔符。如果不传参或传undefined的话默认以逗号作为分隔符。
如果数组中某一项是null或undefined,那么该值在上述四种方法的返回中以空字符串表示。
静态方法
Array.isArray( )
利用Array.isArray( )方法来检测某个值是否是数组。
Array.from()
Array.from() 方法可以将一个类数组或可迭代对象转换成数组。
//Array from a String
Array.from('foo');
// ["f", "o", "o"]
//Array from a Set
let s = new Set(['foo', window]);
Array.from(s);
// ["foo", window]
//Array from a Map
let m = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(m);
// [[1, 2], [2, 4], [4, 8]]
//Array from an Array-like object (arguments)
function f() {
return Array.from(arguments);
}
f(1, 2, 3);
// [1, 2, 3]
Array.of()
Array.of() 方法用于创建数组。
由于Array构造函数在只传入一个参数时有不同的意义:若为数字的话则创建相应大小的数组:
var arr = new Array(7);
//[ undefined,undefined ,undefined ,undefined ,undefined , undefined,undefined ]
但是Array.of() 方法只会将传入的参数作为数组项创建数组:
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
因此Array.of()是一个不用考虑参数类型的创建数组的好实践。
栈方法
ECMAScript提供了可以让数组的行为类似于其他数据结构的方法。
push()
- 可以接收任意数量的参数
- 将他们逐个添加到数组末尾
- 返回修改后的数组长度
pop()
- 从数组末尾移除最后一项
- 返回移除的项
队列方法
shift()
- 移除数组中第一个项并返回该项
结合使用push()和shift(),可以模拟队列来使用数组。
unshift()
- 在数组前端添加任意个项并返回新数组的长度。
同时使用unshift()和pop(),可以反向模拟一个队列。
重排序方法
reverse()
- 反转数组项的顺序
- 返回反转后的数组
sort()
- 按升序排列数组项。
返回排序后的数组
sort会调用每一项的toString方法,然后比较得到的字符串。即使数组相中每一项是数值,sort比较的也是字符串。
- 为了更灵活的排序,sort函数接收一个比较函数作为参数。
- 比较函数接收两个参数
- 第一个应在第二个参数之前:返回负数
- 两个参数相等:返回0
- 第一个应在第二个参数之后,返回正数
- 比较函数接收两个参数
//一个简单的比较函数
function compare(value1,value2){
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
var values = [15,10,5,1,0];
values.sort(compare);
alert(values);//0,1,5,10,15
对于数值类型或者其valueOf方法会返回数值类型的对象类型,可以使用一个更简单的比较函数来实现按数值由小到大排列:
function compare(value1,value2){
return value1-value2;
}
操作方法
concat()
基于当前数组中的所有项创建一个新数组
- 先创建当前数组的一个副本
- 将接收到的参数添加到这个副本的末尾
- 返回新构建的数组
有关参数:
- 没有传递参数的情况下只是复制当前数组并返回副本
- 传递了一个或多个数组,该方法会将这些数组中的每一项都添加到结果数组中。
- 传递的不是数组,这些值会被简单地添加到结果数组的末尾
var colors=["red","blue"];
var colors2 = colors.concat("yellow",["black","white"]); //"red","blue","yellow","black","white"
slice()
基于当前数组中的一或多个项创建一个新数组。不会影响原始数组。
- 接受一或两个参数,分别代表要返回项的起始和结束位置。
- 只有一个参数时,返回从该参数指定位置开始到数组末尾的所有项。
- 有两个参数,返回起始和结束位置之间但不包括结束位置的项。
var colors = ["red","blue","yellow","black","white"];
var colors1 = colors.slice(1); //"blue","yellow","black","white"
var colors2 = colors.slice(1,4); //"blue","yellow","black"
- 如果方法中有一个负数,则用数组长度加上该数来确定相应的位置。如在一个长度为5的数组上调用slice(-2,-1)与调用slice(3,4)得到的结果是相同的。
如果结束位置小于起始位置,则返回空数组。
如果没有传参数的话,会返回原数组的一个拷贝。
splice()
主要用途是删除项以及向数组的中部插入项。
arr.splice(start,deleteCount,item1,item2,……)
- 删除:可以删除任意数量的项,需提供两个参数:
- 要删除的第一项的位置
- 要删除的项数
如splice(0,2)会删除数组中的前两项
- 插入:向指定位置插入任意数量的项,需提供三个参数:
- 起始位置
- 0(要删除的项数)
- 要插入的项
如果要插入多个项,可以再传第四,第五个参数。
splice(2,0,“red”“green”)会从数组的位置2插入两个项。
- 替换: 可以向指定位置插入任意数量的项同时删除任意数量的项,需提供三个参数:
- 起始位置
- 要删除的项数
- 要插入的任意数量的项
插入的项数不必和删除的项数相等
如 splice(2,1,“red”“green”)会删除位置2的一项,再从位置2开始插入字符串。
splice方法始终都会返回一个数组,该数组包含了从原始数组中删除的项,如没有删除任何项则返回一个空数组。
var colors = ["red","blue","yellow"];
var removed = colors.splice(0,1);//red
colors;//"blue","yellow"
removed = colors.splice(1,0,"black","white");//[]
colors;//"blue","black","white","yellow"
removed = colors.splice(1,1,"red","pink");//black
colors;//"blue","red","pink","white","yellow"
请注意,splice() 方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改。
位置方法
indexOf()
lastIndexOf()
这两个方法都接收两个参数:
- 要查找的项
查找起点位置的索引(可选)
前者从数组开头开始查找,后者则从数组末尾开始查找。
都返回要查找的项在数组中的位置,没找到则返回-1。
在比较第一个参数和数组中的每一项时,都使用全等操作符
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.indexOf(4); //3
numbers.lastIndexOf(4); //5
numbers.indexOf(4,4); //5
numbers.lastIndexOf(4,4); //3
var person = {name:"nicholas"};
var people = [{name:"nicholas"}];
var morePeople = [person];
people.indexOf(person); //-1;
morePeople.indexOf(person); //0
//这是由于person和people中的那一项并不是一个对象,因此利用全等比较会不相等,因此返回-1;
//而morePeople是由person得来,其中的那一项与person是同一个对象,因此可以找到
迭代方法
每一个迭代方法都对数组中的每一项运行给定函数,都接受两个参数:
1.要在每一项上运行的函数,此函数接收三个参数
- 数组项的值
- 该项在数组中的位置
- 数组对象本身
2.运行该函数的作用域对象(可选)
every()
如果函数对每一项都返回true,则返回true
some()
如果该函数对任一项返回true,则返回true
filter()
返回该函数会返回true的项所组成的数组
forEach()
无返回值
map()
返回每次函数调用的结果组成的函数
以上这些项都不会修改数组中的值。
- 其中最像的是every()和some(),都用于查询数组中的项是否满足某个条件。
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
return (item >2);
}); //false
var someResult = numbers.some(function(item,index,array){
return (item >2);
}); //true
- filter函数则确定是否在返回的函数中包含某一项
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
return (item >2);
}); //[3,4,5,4,3]
- map则返回函数作用于每一项上的结果所组成的数组
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
return item*2;
}); //[2,4,6,8,10,8,6,4,2]
- forEach只是对数组中的每一项运行传入的函数,没有返回值,本质上与使用for循环遍历数组一样
但并不会修改数组中的值!!
归并方法
reduce()
reduceRight()
从左到右(或从右到左)为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。
都接收两个参数:
- 在每一项上调用的函数(参数其实就比前面那些迭代方法多了个上次调用函数后的值)
- 上次调用回调返回的值
- 当前值
- 当前元素的索引
- 数组本身
- 作为归并基础的初始值(可选)
var total = [0, 1, 2, 3];
total.reduce(function(sum,num){
return sum+num;
},10)
这里指定了初始值10,并与数组的每一项进行相加。
ES6新增方法
copyWithin()
copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置。
arr.copyWithin(target, start, end)
- target:复制序列到此位置。
如果 target 大于等于 arr.length,将会不发生拷贝。 - start:复制序列的起始(忽略的话从0开始)
- end:复制序列的结尾,不包括此位置的元素(如果忽略,则会复制到数组结尾)
注意:start到end之间要被复制的序列会覆盖target开始的对应项,而不是插入
[1, 2, 3, 4, 5].copyWithin(2);
//[1, 2, 1, 2, 3]
//忽略了start,将从0开始;忽略了end,则复制到target位置之前
[1, 2, 3, 4, 5].copyWithin(0, 3);
//[4,5,3,4,5]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
//[4,2,3,4,5]
find()和findIndex()
这两个方法允许我们通过测试函数来获取满足条件的数组项。
- find() 方法返回数组中满足提供的测试函数的第一个元素的值。
- findIndex() 方法返回数组中满足提供的测试函数的第一个元素的值的索引。
function isBigEnough(element) {
return element >= 15;
}
[12, 5, 8, 130, 44].find(isBigEnough); // 130
[12, 5, 8, 130, 44].findIndex(isBigEnough); // 3
这两个方法其实是对数组中的每一项元素执行一次callback 函数,直至有一个callback返回true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回undefined。
keys()、values()和entries()
这三个方法均返回一个数组的Iterator,其value分别包含数组的键名、键值以及键值对。
var arr = ["a", "b", "c"];
var iterator = arr.keys();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
iterator = arr.values();
console.log(iterator.next()); // { value: a, done: false }
console.log(iterator.next()); // { value: b, done: false }
console.log(iterator.next()); // { value: c, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
iterator = arr.entries();
console.log(iterator.next().value); // [0, "a"]
console.log(iterator.next().value); // [1, "b"]
console.log(iterator.next().value); // [2, "c"]
includes()
要想知道一个数组是否包含一个值,之前只能使用indexof方法通过返回值是否为-1来判断,includes方法则会返回一个布尔值来判断一个数组是否包含一个指定的值。
let a = [1, 2, 3];
a.includes(2);
// true
a.includes(4);
// false