3

结合《javascript高级程序设计》《javascript权威指南》《javascript语言精粹》做的一篇关于Array对象的全面解析。分为两篇:基础篇和扩展应用篇。

1.概念及特点

数组是值的有序集合,每个值称为一个元素,每个元素在数组中有特定位置,以数字表示,称为索引,JavaScript中的数组是一个类数组的对象,虽然在性能上比真正的数组会慢,但它使用起来更方便。

特点 说明
元素类型任意性 数组元素可以是基础数据类型,对象,也可以是数组
动态性 根据需要它们会增长或缩减,并且在变化时无需重新分配内存空间
稀疏性 数组元素的索引不一定是连续的,它们之间可以有空缺,

2.创建方式

2.1 数组字面量方式 : 元素用逗号隔开即可。

var empty = [];//创建空数组
var num = [1,2,3,4,5];//创建5个数值类型元素的的数组
var mix = [1,'jozo',true,[1,2],{1:'jozo'}];//创建任意类型元素的数组

2.2 构造函数方式 : 调用构造函数Array(),根据参数不同,创建不同的数组
a.不传递参数

var a = new Arry();//创建一个空数组,但我们更常用下面的方式
var a = [];

b.传递一个数值参数,这个参数用来指定数组长度

var a = new Arry(5);// 创建了一个长度为5的数组

c.传递多个参数,用这些参数作为数组元素初始化数组。

var a = new Arry(1,'jozo',true,[1,2]);//构造函数的参数将会成为数组元素

3.添加与删除

3.1 添加

a.通过索引添加

var a = [];
a[0] = 'jozo';//此时 a = ['jozo']
a[1] = 'blog';//此时 a = ['jozo','blog']

b.通过数组方法添加

push(),concat(),splice(),unshift()方法都可以为数组添加元素,后面将会详细介绍。

3.2 删除

a.删除数组元素

var a = [1,2];
delet a[0];//删除第一个元素
console.log(a[0]);//undefined
console.log(a[1]);//2
console.log(a.length);//2

可以看出,通过delete运算符删除数组元素也有一些注意的地方。1.原数组长度不变。2.被删除的元素的值变为undefined.3.数组内的其他元素的索引没有改变。其实就是原数组变成了稀疏数组

splice(),pop(),shift()数组方法也可以用于删除数组元素,后面讲解。

b.删除整个数组对象

第一种方式:直接操作数组对象(推荐用法)
var a = [1,2];
a.length = 0;
console.log(a);//输出: []

第二种方式:改变变量的引用 (不算真正的删除)
var a = [1,2];
a = [];
console.log(a);//输出: []

3.常用方法属性详解

其实上面的知识点不讲我们都差不多都知道的。但是数组的一些方法属性我们虽然知道但是却不会用,或是总是忘记该怎么用,因为它的方法属性也很多,我们来分析下各个方法的特点:

常用方法 说明 返回值 影响原数组
1.join() 使用不同的分隔符将数组转换成包含分隔符的字符串 转换后的字符串 F
2.reverse() 颠倒数组元素的顺序 重排序的数组 T
3.sort() 通常会接受一个比较函数将数组特定的顺序排序 重排序的数组 T
4.concat() 将传递给该方法的每一个参数添加到原数组中 修改后的数组副本 F
5.slice() 获取当前数组中的一或多个元素创建一个新的数组 返回新的数组 F
6.splice() 通过传递参数不同,可实现数组的增删改 新的数组 T
7.push()/pop() 两个数组的栈方法(后进先出),在数组的末尾增加或删除数组 pop()返回数组长度,push()返回被删除的元素 T
8.unshift()/shift() 两个数组的堆方法(先进先出),在数组的前端增加或删除数组 unshift()返回数组长度,shift()返回删除的元素 T

3.1 join()方法

将数组中的所有元素都转化为字符串链接在一起,可以指定一个可选的分隔符来分隔各个元素,若未指定分隔符,默认使用逗号:

var boys = ['jozo','jozo1','jozo2'];
var newboy1 = boys.join();
var newboy2 = boys.join('+');
var newboy3 = boys.join(undefined);

console.log(newboy1); // jozo,jozo1,jozo2
console.log(newboy2); // jozo+jozo1+jozo2
console.log(newboy3); // jozo,jozo1,jozo2
console.log(boys); // ["jozo", "jozo1", "jozo2"]

从上面的代码可以看出一些问题:1.join()方法传递undefined,也会默认使用逗号分隔符,但是IE7及一下版本会直接用'undefined'作为分隔符。2.join()方法并没有改变原数组。

3.2 reverse()方法
将数组元素颠倒顺序(注意:并不是从大到小或者是从小到大),返回逆序的数组,这个方法直接对原数组中排序。

var a = [1,3,2];
console.log(a.reserse());// [2,3,1] 只是颠倒顺序,不是按大小排序
console.log(a);//[2,3,1] 改变了原数组

这个方法快速直观明了,但不够灵活,很多时候我们需要特定的排序,所以有了下面的更灵活的方法。

3.3 sort()方法

默认情况下,sort()方法按从小到大的排序,但是如果是数值,sort()方法会调用每个元素的toString()方法转换为字符串后再比较:

var num  = [1,5,10,15];
console.log(num.sort()); //[1,10,15,5]  按照字符串比较。

var num  = ['jozo','c','b','a'];
console.log(num.sort()); //['a','b','c','jozo']  按照字符串比较。

默认的sort()方法以字母表顺序进行排序,这有时也不是最佳方案,因此我们可以传递一个函数类型的参数作为比较函数,改变排序方式,以便我们确定哪个值在前面。
比较函数:接受两个参数,函数的返回值决定数组的排序方式。

返回值 排序方式
负数 从小到大
正数 从大到小
0 顺序无关紧要

看下代码:

// 为了直观一点,写个罗嗦的比较函数
var compare = function(a,b){
    if(a < b){
        return -1;
    }else if(a > b){
        return 1;
    }else{
        return 0;
    }
};
var num1  = [1,5,10,15];
console.log(num1.sort(compare)); //[1,5,10,15]  从小到大

var num2  = ['jozo','c','b','a'];
console.log(num2.sort(compare)); //['a','b','c','jozo']  从小到大

// compare()函数可以改进下:
//从小到大的比较函数
var compare = function(a,b){
    return a - b;
};
//从大到小的比较函数
var compare = function(a,b){
    return b - a;
};

//或者直接给sort()方法传递一个匿名比较函数:
num.sort(function(a,b){return a -b}); // 推荐用法

3.4 concat()方法

这个方法先会创建当前数组的一个副本,然后将收到的参数添加到副本数组的末尾,返回重新构建的数组。

1.当没有传递参数时,只是返回当前数组的一个副本。

var a = [1,2];
b = a.concat();
console.log(b);//[1,2] a 的副本
console.log(a);//[1,2]; a 未改变

2.当传递的参数为非数组时,将会把每个参数添加到副本中

var a = [1,2];
b = a.concat(3,4);
console.log(b);//[1,2,3,4] 在a的副本上添加
console.log(a);//[1,2]; a 未改变

3.当传递的参数是数组时,将会把数组的每一个元素添加到副本中。

var a = [1,2];
b = a.concat([3,4]);
console.log(b);//[1,2,3,4] 在a的副本上添加
console.log(a);//[1,2]; a 未改变

//来看看参数的另一种形式
var a = [1,2];
b = a.concat([3,4,[5,6]]); //数组的数组
console.log(b);//[1,2,3,4,[5,6]]  //数组的数组直接添加到副本
console.log(a);//[1,2]; a 未改变

3.5 slice()方法

这个方法返回指定数组的一个片段或子数组,接受一个或两个参数。

1.一个参数 :返回该参数指定位置(包含)到数组末尾的元素的新数组

var a = [1,2,3,4,5];
a.slice(0);// 返回 [1,2,3,4,5]
a.slice(1);// 返回 [2,3,4,5]
a.slice(7);// 返回 [] 参数超过数组索引,返回空数组
a.slice(-1);//返回 [5] 用数组长度-1 相当于slice(4);
console.log(a);//返回 [1,2,3,4,5] 原数组不变

2.两个参数 :参数作为始末位置,但不包含第二个参数指定的位置。

var a = [1,2,3,4,5];
a.slice(0,4);// 返回 [1,2,3,4]
a.slice(1,4);// 返回 [2,3,4]
a.slice(1,7);// 返回 [2,3,4,5] 参数超过数组索引,则到数组末尾
a.slice(1,-1);//返回 [2,3,4] 用数组长度-1 相当于slice(1,4);
a.slice(1,-7);//返回 [] 当结束位置小于起始位置,返回空数组
console.log(a);//返回 [1,2,3,4,5] 原数组不变

3.6 splice()方法

这个数组恐怕是数组里最强大的方法了,它有多种用法,主要用途是向数组中部插入元素,请不要和上面的slice()方法混淆了,这是两个完全不同的方法。由参数的不同,可实现下列三种方法:

1.删除 :指定一个或两个参数,第一个参数是删除的起始位置,第二个参数是要删除的元素个数,若省略第二个参数,则从起始位置删除至末尾:

var a = [1,2,3,4,5];
a.splice(3,2);//返回 [4,5] 从索引3开始,删除2个元素,此时 a = [1,2,3]
a.splice(1);// 返回 [2,3] 此时 a = [1]

2.插入:指定3个及以上个参数,前两个参数和上面的一致,第二个参数一般为0,后面的参数表示要插入的元素:

var a = [1,2,3,4,5];
a.splice(4,0,6,7);//返回 [] 从索引4开始,删除0个元素,此时 a = [1,2,3,4,5,6,7]

//下面这种情况又和concat()不同,直接插入数组而非数组元素
a.splice(4,0,[6,7]);//返回 [] 从索引4开始,删除0个元素,此时 a = [1,2,3,4,5,[6,7]]

3.更新:指定3个及以上个参数,前两个参数和上面的一致,第二个参数指定要删除的元素个数,后面的参数表示要插入的元素:

var a = [1,2,3,4,5];
a.splice(3,2,6,7);//返回 [4,5] 从索引3开始,删除2个元素,此时 a = [1,2,3,6,7]

3.7 push()/pop()方法

补充下数据结构的知识,栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入和移除只发生在栈顶部。数组的push(),pop()方法就为数组实现了类似栈的功能:

1.push():该方法可以接受任意数量,任意类型的的参数,并将它们添加至数组的末尾(栈顶),最后返回修改后的数组的长度

var a = [];// 创建空数组
var lng = a.push(1,2,3);// 添加数组元素
console.log(a);// 输出:[1,2,3]
console.log(lng);// 输出:3  返回数组长度3
var lng2 = a.push(4,[5,6]);//
console.log(lng2); // 输出:5  返回数组长度5
console.log(a);//输出:[1,2,3,4,[5,6]]

2.pop() :相反,该方法删除数组的最后一个元素,减小数组长度,并返回删除的元素。不带参数。

var a = [1,2,3];
var last= a.pop();// 删除数组最后一个元素
console.log(a);// 输出:[1,2]
console.log(last);// 输出:3  被删除的元素是 3

可以看出,这两个方法都是直接修改原数组。

3.8 unshift()/shift()方法

上面提到了栈的数据结构,这里再提一个队列的数据结构,这是一种FIFO(First-In-First-Out,先进先出)的数据结构,队列添加元素是在末端,删除是在前端。很多同学就会猜测了,unshift()就是在末端添加元素,shift()就是在前端删除元素,其实不然:

1.shift():用于在前端删除数组元素,返回被删除的元素,与push()方法结合便是一对队列方法。

var a = [1,2,3];
a.push(4,5);//此时 a = [1,2,3,4,5] 
var start = a.shift();//此时 a = [2,3,4,5] 删除最前端的元素
console.log(start);// 1 返回删除的元素

2.unshift():用于在前端添加元素,返回修改后的数组的长度,与pop()方法结合便是一对反操作的队列。

var a = [1,2,3];
a.unshift(4,5);//此时 a = [4,5,1,2,3] 在前端添加元素
var end= a.pop();//此时 a = [4,5,1,2] 
console.log(end);// 3 返回删除的元素

这两个方法同样都是直接修改原数组。

4.ES5的数组方法

ECMAScript定义了9个操作数组的数组方法:

遍历:forEach()
映射:map()
过滤:filter()
检测:every(),some()
简化:reduce(),reduceRight()
搜索:indexOf(),lastIndexOf()

每个方法都接受两个参数:1.要在每个数组元素上运行的函数;2.运行函数的作用域对象 -- this指向 (可选参数)
第一个参数--函数又可传递三个参数(简化和搜索方法除外),分别代表:1.每个数组元素的值;2.元素的索引;3.数组本身

注意:所有这些方法都不会修改原始数组,但是传递的函数是可以修改的。

4.1 forEach()
该方法对数组的每一项运行给定的函数。这个方法没有返回值。

var nums = [1,2,3];
var sum = 0;
nums.forEach(function(value){sum += num;}); //没有对原数组进行修改
console.log(sum); // 6  1+2+3

nums.forEach(function(value,i,ary){ary[i] = value +1;}); //对数组进行了修改
console.log(nums);//[2,3,4]

4.2 map()
该方法对数组的每一项运行给定的函数,返回每次函数调用的结果组成的数组。

var nums = [1,2,3];
var squer = nums.map(function(value){return value*vlaue});
console.log(squer); // [1,4,9]
console.log(nums);// [1,2,3]

注意:这可能看起来有点像forEach()方法,但细看会发现 该方法有返回值,而前者没有,而且返回值是数组,这个数组是新数组,并没有对原始数组进行修改。如果原始数组是稀疏数组,返回的也是相同方式的数组,具有相同的长度和相同的缺失元素。

4.3 filter()
该方法对数组的每一项运行给定的函数,返回该函数会返回true的项组成的数组。

var a = [1,2,3,4,5];
smallValue = a.filter(function(value){return value < 3;});// [1,2]

注意:filter()会跳过稀疏数组中缺少的元素,他的返回数组总是稠密的。下面的方式可以压缩稀疏数组的看空缺:

var a = [1,,3,,5];//有两个空缺元素
Var uglify = a.filter(function(){return true;}); //[1,3,5]

还可以过滤undefined和null的元素:

var a = [1,undefined,3,,null,5];//有两个空缺元素
Var uglify = a.filter(function(value){
    return value != undefined && value != null;
}); //[1,3,5]

4.4 every(),some()
every():对数组的每一项运行给定的函数,如果该函数对数组的每一项都返回true,则返回true,注意是每一项,有一项为false则为false.

var nums = [1,2,3,4,5];
var bigresult = nums.every(function(value){return value > 2}); // false 不全大于2
var result = nums.every(function(value){return value > 0}); //true 全部大于0

some():对数组的每一项运行给定的函数,如果该函数对数组的任一项返回true,则返回true。

var nums = [1,2,3,4,5];
var bigresult = nums.every(function(value){return value > 2}); // true 有大于2的元素
var result = nums.every(function(value){return value < 0}); //false 全部大于0

注意:在数组是空数组时,every()返回true,some()返回false

clipboard.png

4.5 reduce(),reduceRight()
这两个方法都会迭代数组的所有项,然后构建一个最终的返回值。reduce()从数组的第一项开始,逐个遍历到最后;reduceRight()从数组的最后一项开始,逐个遍历到第一项。

这两个方法都是接收两个参数,一个是在每项上调用的函数,另一个是作为遍历的初始值。调用的函数又接收四个参数,分别是:前一个值,当前值,索引,数组对象。这个函数的返回值都会自动作为下一次遍历的函数的第一个参数。若未指定初始值,第一次遍历发生在数组的第二项上,因此第一个参数就是数组第一项,第二个参数就是数组的第二项。我们来个求和运算:

var nums = [1,2,3,4,5];
nums.reduce(function(pre,cur,index,ary){return pre + cur;}); // 15 

//指定初始值,则第一个参数就是初始值,第二个参数就是数组第一项
nums.reduce(function(pre,cur,index,ary){return pre + cur;},10); 25

在简单的数字元算上,reduce()和reduceRight()除了顺序不同,其他的完全相同。

4.6 indexOf(),lastIndexOf()
这两个方法都接受两个参数:要查找的项,查找起点位置的索引(可选);indexOf()从数组头部开始检索,lastIndexOf()则从数组尾部向前开始检索。
两个方法都都返回找到的元素的第一次出项的位置(索引),在没有找到的情况下返回 -1 。
要注意的是:在检索时会与数组的每一项进行全等的比较,也就是必须严格相等(===)。

var nums = [1,2,3,4,5,4,3,2,1];
console.log(nums.indexOf(3)); // 2  索引为2
console.log(nums.lastIndexOf(3)) // 6 从后面开始找,索引为6;

console.log(nums.indexOf(3,3)); // 6  从位置3开始向后找
console.log(nums.lastIndexOf(3,3)) // 2 从位置3开始向前找

console.log(nums.indexOf(6)); // -1  没有找到

var class= {name : 'ruanjian'};
var students = [{name : 'jozo'}];
console.log(students.indexOf(class)); //false  非严格相等(不是同一个对象)

var school = [class];
console.log(school.indexOf(class);); //true 严格相等(同一个对象)

5.总结

结合高级程序设计与权威指南两本书,内容比较多,写了好长,写的过程中有种觉得没必要的感觉,但是写完之后就会觉得很有价值,至少对我来说。不是我不会,而是记得不深刻,重新书写一遍之后感觉对数组这东西比较透彻了。我也建议各位多做一个学习总结,如有不正确的,请提醒修正。谢谢。下一篇文章继续介绍数组!关于ES6的一些扩展以及数组一些应用。


ouou
8.7k 声望472 粉丝

进击的前端er...