本篇文章主要讲述ES5中的数组,包括数组两种创建方式,属性,以及 9 大类 ,总共23个操作方法,非常全面,看完之后ES5数组这一部分基本都了解了,下一篇文章,我会讲述ES6中对数组的加成,新增了哪些方法,以及定型数组,类数组和类数组的所有方法,记得关注哦!
数组作为javascript中最常用的数据类型之一,掌握好数组的方法在日常的开发中是非常有必要的,javascript中的数组相比其他语言来说更灵活,因为数组的每一项都可以是不同的数据类型,可以是对象,数组,字符串,数值等等,接下来一点一点的了解吧!
1.数组的创建
说到数组首先肯定是创建,只有创建或者声明一个变量是数组之后才可以使用它,以及他的一些属性和方法。数组的创建有两种方式,即,构造函数式和数组字面量式。
-
第一种构造函数式创建,利用new Array来创建,下面先上一段例子:
var arr = new Array();
var arr1 = new Array(3);
var arr2 = new Array("xiao mage");
var arr3 = new Array(12,34,);
var arr4 = new Array(,,,,,);
console.log(arr); //[]
console.log(arr1); //[empty*3]
console.log(arr2); //["xiao mage"]
console.log(arr3); //在谷歌浏览器中报错,不能多逗号,注意
console.log(arr4); //在谷歌浏览器中报错,这样也是不行的分析一下,第一行创建了一个空数组;第二行创建了一个长度为3的空数组,注意这个3是数组的长度为3,不是数组的第一项是数值3,切记!在下一篇讲到的类数组方法Array.of()方法就可以创建数值3;第三个是创建了一个字符串数组,里面只有一个值;第四行,第五行都目前chrome浏览器无法编译通过,写法有问题,特意列出来注意一下。 在使用Array 的时候new也可以省略,其效果也是一样的。
-
第二种是数组字面量创建数组,即直接用[]。
var arr = []; //[]
var arr1 =[12,34,]; //[12,34]
var arr2 = [,,,,] ; //[empty*4]
var arr3 = ["xiaomage"]; //["xiaomage"]
var arr4 = [1,2,3,4,5]; //[1,2,3,4,5]
var arr5 = [1,{"a":1,"b":2},[434,798],"xiaomage"];
console.log(arr); //[]
console.log(arr1); //[12,34]
console.log(arr2); //[empty*4]
console.log(arr3); //["xiaomage"]
console.log(arr4); //[1,2,3,4,5]
console.log(arr5); //[1, {…}, Array(2), "xiaomage"],展开就是[1,{"a":1,"b":2},[434,798],"xiaomage"]再分析一下,第一行也是创建了一个空数组;第二行创建了一个长度为2的数组,数组中包含12,34,在有的浏览器中长度为3,注意一下。第三行创建了一个长度为4的空数组,数组的每一项值是undefined,有的浏览器也可能数组的长度是5。第四行创建了一项含有字符串的数组;第五行创建一个全是数值的数组,最后一行创建了一个长度为4的数组,每一项都是不同的数据类型。
注意: 1. var arr = new Array(,,,,);是报错的,而var arr = [,,,,];是可以创建数组的;以及var arr = new Array(12,34,)也是报错的,而var arr = [12,34,]是可以的,这些注意不要搞混淆了,日常工作中都不推荐此种方法创建数组; 2. 虽然两种方法都可以创建数组,但是数组字面量要比构造函数的性能好,原因是:json格式的语法浏览器引擎能直接解析,而new Array需要调用Array的构造器。日常工作中可以使用前者,方便快速省性能。
2. length属性
大家都知道length属性表示数组的长度为多少,现在用构造函数式和数组字面量式创建一个长度为10的空数组,试试看:
var arr = new Array(10);
console.log(arr.length) ; //10
var arr1 = [];
console.log(arr1.length); //0
arr.length = 10;
console.log(arr); //[empty*10]
var arr2 = [,,,,,,,,,,];
console.log(arr2.length); //10
现在用数组的length属性来操作数组的长度,不用arr.pop()和arr.shift()方法怎么来搞呢?
var arr1 = [0,1,2,3,4,5,6,7,8,9];
console.log(arr1.length); //10
arr1.length = 8;
console.log(arr1); //[0,1,2,3,4,5,6,7]
var arr2 = [1,2];
arr2.length = 100;
console.log(arr2) ; //[1, 2, empty × 98]
console.log(arr2[10]); //undefined
arr2[99] = 'xiaomage';
console.log(arr2) //[1, 2, empty × 97, "xiaomage"]
console.log(arr2.length);
arr2[arr2.length] = 1100000;
console.log(arr2); //[1, 2, empty × 97, "xiaomage",1100000]
分析一下:首先arr1 是一个长度为10的数组,当把数组的长度成为8的时候,此时会从数组的第一个元素开始向后截取8个作为数组的新值,此操作会改变数组。 arr2是一个长度为2的数组,将数组的长度设为100后,将依次为数组的剩余97项设置值为undefined,当再改变某一项的元素的时候就是正常的赋值了,最后一个给数组添加一项,当前数组的最后一项的索引是length-1,当arr.length就是添加一项。
3.数组的检测
数组作为引用类型之一,检测方法有两种:instanceof和isArray。其中instanceof适用于在一个网页或者一个全局作用域的情况比较实用,随着前端框架越来越多,在项目 中引入太多的框架的时候,每个框架对数组可能进行过二次封装,这样导致一个问题就是instanceof 返回的结果可能会不同,使用时得注意,基本用法如下:
var arr = [] ;
console.log(arr instanceof Array); //true
基于以上的问题,ES5又新增了新的检测方法,isArray 来确定某个值到底是不是数组,而不用管其他的作用域问题。写法如下:
var arr = [];
console.log(Array.isArray(arr)); //true
4.类栈操作相关
数据结构栈的特点就是后进先出(LIFO, last-in-first-out) ,比喻成容器罐好理解点,最后放的在上面,拿的时候也先从上面拿,操作推入和弹出,都发生在栈的顶部,javascript数组中有专门的方法push()和pop()来模拟栈的操作。
push()方法,接受不限制个数的参数,在数组的末尾插入元素,返回值是修改后的数组的长度,此方法会改变原数组;pop()方法,不接受参数,从数组的末尾来删除最后一个元素,返回值是删除的元素,此方法也会改变原数组。下面我们来看看具体用法:
var arr = [92,09,13,'xiaomage'];
console.log(arr.length); //4
var count = arr.push('daqianduan',89);
console.log(count); //6
console.log(arr); //[92, 9, 13, "xiaomage", "daqianduan", 89]
var item = arr.pop();
console.log(item); //89
console.log(arr); //[92, 9, 13, "xiaomage", "daqianduan"]
5.类队列操作相关
上面聊完了栈,现在来看看队列,相信这两个词大家都不陌生,队列的特点是:先进先出(FIFO , first-in-first-out),看做是一个空的管道比较好理解,从一端进入,另一端出去。javscript数组提供了shift()和push()方法来模拟队列的特点。
shift() , 从数组的头部删除一个元素,返回值是删除的元素,此方法会改变原数组,看看使用情况:
var arr = [25,78,{'a':1,'b':10},'javascript'];
var count = arr.push([12,34]);
console.log(count); //5
console.log(arr); //[25, 78, {…}, "javascript", Array(2)]
var item = arr.shift();
console.log(item); //25
console.log(arr); //[78, {…}, "javascript",Array(2)]
console.log(arr.length); //4
跟shift()方法结果刚好相反的是unshift()方法,shift()是从数组头部删除元素,而unshift()刚好是从头部添加元素,接受不限个数的参数,返回值是改变后的数组的长度,此方法会改变数组,其实,这个方法效果跟push()一样,只不过一个在数组前面插入,一个在后面插入,看看效果:
var arr = ['xiaomage',[15],90,23];
arr.push('string1',10);
console.log(arr); //["xiaomage", Array(1), 90, 23, "string1", 10]
var count = arr.unshift([34,45],'string2');
console.log(count); //8
console.log(arr); //[Array(2), "string2", "xiaomage", Array(1), 90, 23, "string1", 10]
arr.shift();
console.log(arr); //[Array(2), "string2", "xiaomage", Array(1), 90, 23, "string1", 10]
arr.pop();
console.log(arr); //["string2", "xiaomage", Array(1), 90, 23, "string1"]
6.数组的重排序方法
说到数组的重排序方我们肯定想到的是reverse()和sort()方法,来了解一下这两个方法,首先:
reverse()方法是将数组进行反转,调换顺序,比较简单哈,返回结果是改变顺序后的数组,此方法会改变原数组。
var arr = [0 ,1 ,2 ,4,5,10,15];
var arr2 = arr.reverse();
console.log(arr2); //[15, 10, 5, 4, 2, 1, 0]
console.log(arr); //[15, 10, 5, 4, 2, 1, 0]
即返回的结果就是原数组改变后的结果,二者是相等的,但是这个方法只能反转不方便啊,比如说我想把数组按照升序或者降序排列,reverse就干不了,这时候就得用到sort()方法了。
sort()方法,默认是按照升序排列的,返回的结果也是改变后的数组,此方法会改变原数组。
var arr = [0,1,10000000,15,10,5];
var arr1 = arr.sort();
console.log(arr); //[0, 1, 10, 10000000, 15, 5]
console.log(arr1); //[0, 1, 10, 10000000, 15, 5]
首先,同学们可能会疑问,怎么返回的结果跟我预想的不一样呢,不应该是[0 , 1 , 5, 10 ,15 , 10000000]吗?这是因为当调用sort()方法的时候,首先sort()会把数组的每一项值都先调用toString()方法变成对应的字符串,即['0','1','10000000','15','10','5'];第二步再进行字符串的比较,挨个字符比较。上一篇《一篇文章搞定javascript中的字符串》我讲过字符串的比较方法,不了解的回过去看一下哈,然后得出结果[0, 1, 10, 10000000, 15, 5]。
这里同学可能就有疑问,我就想要结果[0 , 1 , 5, 10 ,15 , 10000000]。也不是不可以,这时候我们可以使用sort()方法的参数来进行处理,此方法接受一个比较函数来作为一个参数,里面我们可以自定义我们想要的结果,是想让升序呢,还是降序都可以实现。
var arr = [0,1,10000000,15,10,5];
var arr1 = arr.sort(compareArray);
console.log(arr1); //[0, 1, 5, 10, 15, 10000000]
console.log(arr); //[0, 1, 5, 10, 15, 10000000]
function compareArray(item1,item2){
if(item1 < item2 ){
return -1 ;
}else if(item1 > item2 ){
return 1 ;
}else{
return 0 ;
}
}
是的,我们想要的结果得到了,这里解释一下compareArray,次方法作为sort()方法的参数,接受两个参数,分别是要比较的数组的两项,返回结果是三种,大于0 ,小于0,或者等于0.然后sort根据返回的结果,如果小于零则不用换位置,如果大于零则需要调换位置,等于零则代表相等,不做处理,进行下一次的比较。
前面我们看了升序的写法,那么降序也是一样的,只不过把返回值调换一下就可以了。
var arr = [0,1,10000000,15,10,5];
var arr1 = arr.sort(compareArray);
console.log(arr1); //[10000000, 15, 10, 5, 1, 0]
console.log(arr); //[10000000, 15, 10, 5, 1, 0]
function compareArray(item1,item2){
if(item1 < item2 ){
return 1 ;
}else if(item1 > item2 ){
return -1 ;
}else{
return 0 ;
}
}
//这里我只是把sort的参数方法提出来了,下面这样写也是等价的。
var arr2 = arr.sort(function(item1 , item2){
if(item1 < item2 ){
return 1 ;
}else if(item1 > item2 ){
return -1 ;
}else{
return 0 ;
}
});
console.log(arr2);
看着一个数组的比较,写了这么多的代码,有没有高级写法,简写的让自己的代码看起来更有逼格一点呢,当然有啊,这里我们用到ES6来改写一下:
var arr = [0,1,10000000,15,10,5];
arr.sort((item1 , item2)=> item1 - item2);
console.log(arr); //[0, 1, 5, 10, 15, 10000000]
简单明了,这是升序的结果,如果想要降序用item2 - item1就可以。这里稍微解释一下,第一,箭头函数是原始函数的简写,方便轻巧,第二,当箭头函数中代码块中只有一行语句并且包含return时,可以省略{}和return。关于ES6后面的文章中我会在后面的文章中写,记得关注。
7.数组的转换方法
数组的转换方法有三个toString(),toLocaleString(),valueOf().
toString(),将数组转换成字符串 ,返回值是改变后的字符串,此方法不会改变数组。
var arr = [1, 'xiaoma' , [1,2,3], 10000];
var str = arr.toString();
console.log(arr); //(4) [1, "xiaoma", Array(3), 1000]
console.log(str); //1,xiaoma,1,2,3,1000
toLocaleString(),在有的时候,结果和toString()一样,当然也不总是 一样,,这得跟当地的编码有关,使用时也得注意,此方法转换时每一项都调用toLocaleString()。
var arr = [1, 'xiaoma' , [1,2,3], 1000000];
var str = arr.toLocaleString();
console.log(arr); //[1, "xiaoma", Array(3), 1000000]
console.log(str); //1,xiaoma,1,2,3,1,000,000
var arr2 = str.split(',');
console.log(arr2); //(8) ["1", "xiaoma", "1", "2", "3", "1", "000", "000"]
在我们地区,超过三位以上的数字会以 ’,‘ 隔开的。所以得到的结果会和toString()不同。
valueOf(),该方法是数组对象的默认内置方法,返回Array对象的原始值,该原始值由Array对象派生的所有对象继承,此方法不会改变原数组。
var arr = [1, 'xiaoma' , [1,2,3], 1000000];
var arr2 = arr.valeuOf();
console.log(arr); //[1, "xiaoma", Array(3), 1000000]
console.log(arr2); // [1, "xiaoma", Array(3), 1000000]
注意:所有的对象含有toString(),toLocaleString(),valueOf()这三个内置方法,并不是数组独有。
8.数组的操作方法
- concat():顾名思义,连接,将一些数组拼接到一起形成一个新的数组,此方法不改变原数组。可以接受任意多个参数,参数可以是数组,字符串,数值都可以。当不传参数的时候只返回原数组的副本。
var arr = ['xiaoma' , 324, 90, 33] ;
var arr1 = arr.concat();
var arr2 = arr.concat('javascript',[1,3,,4],[5,6,7],10);
console.log(arr); //["xiaoma", 324, 90, 33]
console.log(arr1); //["xiaoma", 324, 90, 33]
console.log(arr2); //["xiaoma", 324, 90, 33, "javascript", 1, 3, empty, 4, 5, 6, 7, 10]
- slice():即截取,接受一个或者两个参数,第一个参数是要截取的起始项,第二个参数是截止项。当只有一个参数的时候,默认是从起始项到数组末尾,该方法返回的是一个新数组,不改变原数组。
var arr = ['xiaoma', [1,2,3],10,45,{'a':1,'b':2},'javascript'];
var newArr = arr.slice(1);
var newArr2 = arr.slice(2,4);
console.log(arr); //["xiaoma", Array(3), 10, 45, {…}, "javascript"]
console.log(newArr); //[Array(3), 10, 45, {…}, "javascript"]
console.log(newArr2); // [10, 45]
当slice的参数中含有负数的参数的时候,则用数组的长度+负参数作为新的参数值再开始截取,计算后的第二个参数比第一个参数小时,则返回的是一个空数组。
var arr = ['xiaoma', [1,2,3],10,45,{'a':1,'b':2},'javascript'];
var newArr = arr.slice(-3,-2); //重新计算相当于slice(3,4)
console.log(newArr); //[45]
var newArr2 = arr.slice(-3,-4); //slice(3,2)
console.log(newArr2); //[]
-
splice(): slice 是截取数组,而splice是向数组中插入元素或者删除元素,或者替换元素,参数的个数的不同起到的作用也不同。
- 删除元素,两个参数,第一个是起始位置,第二个是要删除的个数。此方法会改变原数组,返回值是删除的新数组,不改变原数组。
var arr = [1,2,3,4,5,6,7,8];
var item = arr.splice(3,3);
console.log(item); //[4, 5, 6]
console.log(arr); //(6) [1, 2, 3, 7, 8]
2.插入元素,三个参数,第一个参数是起始位置,第二个参数删除的个数为0,第三个是要插入的元素,如果要插入多个项,则会有第四个,五个参数,总之,从第三个开始都是要插入的项数,返回值是[],会改变原数组。
var arr = [1,2,3,4,5,6,7,8];
var item = arr.splice(4,0,"xiaomage");
console.log(arr); //(9) [1, 2, 3, 4, "xiaomage", 5, 6, 7, 8]
console.log(item); //[]
var item2 = arr.splice(5,0,[10,23,45],"js","web");
console.log(arr); //(12) [1, 2, 3, 4, "xiaomage",[10,23,45], "js", "web", 5, 6, 7, 8]
console.log(item2); //[]
- 替换元素,三个以上的参数,其中第二个参数不能为0,否则就是插入元素了,先删除后替换。返回值是删除原数组的元素所组成的数组
var arr = [1,2,3,4,5,6,7,8];
var item = arr.splice(3,2,100000,10000001,10002);
console.log(arr); //(9) [1, 2, 3, 100000, 10000001, 10002, 6, 7, 8]
console.log(item); // [4, 5]
分析一下,首先是从第三个开始删除了两项,得到[4,5],然后再在此位置上插入了三项,删除项和替换项不一定要相等。
9. 查找位置相关的方法
javascript数组提供查找位置的方法有两种,indexOf()和lastIndexOf(). 查找的时候当数组中有重复的元素是只返回首次出现的元素的位置。
- indexOf(): 如果在数组中能查找到则返回其索引值,如果找不到则返回-1。此方法接受两个参数,第一个参数是要查找的元素,第二个参数是查找的起始位置, 如果没有第二个参数则默认从0开始查, 如果第二个参数是负数的话,则将其作为数组末尾值得一个抵消,即-1表示从最后一个元素开始向后查找,-2表示从倒数第二个元素查找。 如果第二个参数大于数组的长度,表示 已经超出查找范围了,直接返回-1。一直都是从前向后查找,跟参数是不是 负数,查出范围没关系,此方法不会改变原数组。
- lastIndexOf(): 与indexOf()不同的是这个方法是从后向前查找,其他的都一样。
var arr = [1, 2, 3, 100000, 10000001,[34,45],6,3 ,10002, 6, 7, 8];
var count = arr.indexOf(6);
var count5 = arr.indexOf(6,100);
var count1 = arr.indexOf([34,45]);
console.log(count); //6
console.log(count5) //-1
console.log(count1); //-1
var count2 = arr.indexOf("xiaoamge");
console.log(count2); //-1
var count3 = arr.lastIndexOf(3);
console.log(count3); //7
var count4 = arr.lastIndexOf([34.45]);
console.log(count4); //-1
10.数组的迭代方法
数组的迭代在ES5中是有5个,分别是every,filter,forEach,map,some,这五个方法接受两个参数,第一个是函数,是要作用在数组的每一项上面的函数,第二个参数是作用域对象,可选,默认是this即数组对象本身。第一个参数函数接受三个参数,分别是数组的每一项,第二个是索引,第三个是当前的数组。下面分别来看一下其含义和用法:
- every() : 英文意思,简单理解是每一个,即作用在数组每一项上面的函数的返回都为true的时候,Array.every才返回true,此方法不会改变原数组。看一个例子,有一个学生的成绩数组,判断是不是都及格了,看看怎么写:
var arr = [99,57,67,88,100,78];
var result = arr.every(function(item, index ,array){
return item > 60 ;
});
console.log(result); //false
console.log(arr); //[99,57,67,88,100.78]
var arr2 = [100,98,87,76,61];
var result2 = arr2.every(function(item, index , array){
return item >60 ;
});
console.log(result2); //true
- filter() : 过滤,即把符合指定条件的过滤出来,比较简单,看看例子,把及格的学生筛选出来:
var arr = [99,57,67,88,60,100,78];
var newArr = arr.filter(function(item , index ,array){
return item >= 60 ;
});
console.log(newArr); //[99, 67, 88, 60,100, 78]
- forEach() : 遍历数组的每一项然后进行后续的逻辑操作 , 此方法没有返回值。
var arr = [99,57,67,88,60,100,78] ;
var newArr = [] ;
arr.forEach(function(item , index ,array){
if(item >=60){
newArr.push('及格') ;
}else {
newArr.push('不及格') ;
};
});
console.log(newArr); //["及格", "不及格", "及格", "及格", "及格", "及格", "及格"]
- map() : 遍历整个数组的每一项,返回指定操作后的结果。
var arr = [99,57,67,88,60,100,78] ;
var newArr = arr.map(function(item , index ,array){
//return item * 10 +10 ; //[1000, 580, 680, 890, 610, 1010, 790]
return item>=60 ? '及格' : '不及格' ;
});
console.log(newArr) ; //["及格", "不及格", "及格", "及格", "及格", "及格", "及格"]
- some() :此方法跟every()不同的是,every只有都满足条件的时候才返回true ,some是只要有一个满足条件就返回true。every 类似于 && ,而some 类似于 ||。看个例子,查看班级里面有没有成绩超过90分的同学,并且计算有几个:
var arr = [45, 60 , 80, 90 ,99 ,94];
var count = 0 ;
var result = arr.some(function(item , index , array){
return item >90 ;
});
console.log(result) ; //true
var count = arr.filter(function(item , index ,array){
return item >=90;
}).length;
console.log(count) ; //3
11.数组的缩小方法
ES5中提供了两个数组缩小的方法,reduce()和reduceRight(), 两个方法都会遍历数组的所有项,然后返回一个操作后的最终结果。这两个方法都接受两个参数,第一个,作用在数组项上的函数,第二个,是 作为缩小基础的初始值,可选。
- reduce() : 接受四个参数,分别是前一个值(prev),当前值(cur),索引 (index),以及当前数组本身(array),参数名字随便起。当reduce的第二参数有值的时候,第一次遍历的时候,函数的的第一个参数(prev)就是这个初始值,没有值时,第一次遍历的时候前一个值(prev)和当前值(cur)分别是数组的前两项。有点斐波那契数列的感觉,遍历的时候是从前向后。来看一个字符串拼接操作应用:
var arr= ['hello' ,'_this' , '_is' , '_javascript' ,'_Array'];
var result = arr.reduce(function(prev, cur ,index , array ){
console.log(prev , cur) ;
// xiomage_ hello
// xiomage_hello _this
// xiomage_hello_this _is
// xiomage_hello_this_is _javascript
// xiomage_hello_this_is_javascript _Array
return prev + cur ;
},'xiomage_');
console.log(result); //xiomage_hello_this_is_javascript_Array
- reduceRight() : 和reduce唯一不同的是从后向前遍历,类似于indexof和lastIndexOf()的查找顺序,一个前面开始,一个从后面开始。
var arr = [1,2,3,4,5,6];
var result = arr.reduceRight(function(prev,cur,index,array){
return prev + cur ;
});
console.log(result); //21
好了,ES5数组的所有的方法已经讲完了,总共9大类,23个函数,你都了解了吗?
下一篇文章,我会讲述ES6中对数组的加成,新增了哪些方法,以及定型数组,类数组和类数组的所有方法,记得关注哦!