数组专题

目 录

1 数组创建方法 3

2 数组基本操作 3

2.1 存取数组元素 3

2.2 增加数组 4

2.3 删除数组 4

2.4 遍历数组 4

3.ECMAScript 3 方法 4

3.1 添加元素 4

3.2 删除元素 5

3.3 子数组处理 5

3.4 排序方法 6

3.5 数组转换 7

4.ECMAScript 5 方法 7

4.1 位置方法 7

4.2 迭代方法 8

4.3 缩小方法 9

4.4 数组检测方法 10

5.数组去重 10

5.1 数组内单一数据类型去重 10

5.2 数组内复杂数据类型去重 12

5.3 类数组对象转化成数组去重 13

5.4字符串转化成数组去重 14

6.排序算法 15

6.1 基本数组排序算法 15

6.2 DOM对象排序之表格排序 17

7.数组克隆 19

 

数组的概念相信大家都并不陌生了,几乎所有的编程语言都要用到数组来处理数据。但是,JavaScript数组与其他多数语言中的数组有着相当大的区别,JavaScript数组的每一项可以保存任何类型的数据,可以是对象也可以是其他数组,而且数组的大小还可以动态调整。  

JavaScript数组是JavaScript对象的特殊形式,我们称之为Array类型。数组既然是类,就可继承Array.prototype(数组的原型对象)中的属性,我们也可以通过原型对象来自定义许多我们需要的原生方法。数组是ECMAScript中除object外最常用的类型了,由此可看出它的重要地位,既然如此,那么现在我们开始来接触这个熟悉而且陌生的朋友,去领略它在JavaScript中灵活而又便捷的风采。

1 数组创建方法

a) 创建一个空数组

构造函数写法:var arr=new Array() ;简单写法:var arr=[];

b) 创建一个指定长度数组

构造函数写法:var arr=new Array(size) ;简单写法:var arr[size]=[];

c) 创建一个指定元素数组

构造函数写法:var arr=new Array(元素1,元素2….元素n) ;

简单写法:var arr=[元素1,元素2…元素n];

d) 创建一个单维数组

var arr=[元素1,元素2…元素2];

e) 创建一个多维数组

var arr=new Array([数组序列1],[数组序列2]….[数组序列n]);

一般地,数组的维度只用到二维数组和一维数组,掌握这两种即可。一维数组比较容易理解,二维的数组使用比较灵活,有时还显得抽象。二维数组类似宾馆的房间,如果一层有10个房间,而一个宾馆有5层楼,那么我们习惯用第几层第几个房间来对应房间,同样的,二维数组也是这个概念。

2 数组基本操作

2.1 存取数组元素

a) 存取一维数组:数组名[下标索引];

b) 存取二维数组:数组名[外层数组下标][内层元素下标];

c) 数组元素的相关特性:

  1. 要注意的是,数组的下标是从0开始的;
  2. 数组的长度length是弹性的,可伸缩的,改变数组中元素的个数,length会相应变化,改变length的值,同样也会增删数组中元素的个数
  3. 数组的下标类型可以为数值,也可以为非数值,这一点应用会很灵活,要特别注意,下面详细来说一下:下标为数值,就是表示数组中的某一项元素;而当下标为非数值时,js会将下标转换成字符串,生成关联的数组,下标将会作为对象的属性名。

4. 每个数组都有一个length属性,length属性代表数组中元素的个数,如果length属性值为n,那么数组的最大索引值为n-1。掌握length属性需要关注它的两个特殊行,看以下代码:

1 a) var arr1=[1 ,2,3,4,5];
2 
3 b) arr1.length=3; //现在arr的长度为3,后面的两个元素被删除了,那么arr=[1,2,3];
4 
5 c) var arr2=[1,2];
6 
7 d) arr2[2]=3;  
8 
9 e) arr2.length; //此时,length值为3,增加了元素后,length属性的值就被自动更新了

2.2 增加数组

为数组添加元素最简单的方法就是为新索引赋值:

1 var colors=[‘red’,’blue’,’green’];
2 alert(colors[0]);
3 colors[3]=’pink’;

另外,也可以用push()和unshift()方法来在数组的开头和结尾处增加数组元素。

2.3 删除数组

delete运算符比较通用,其他的编程语言也有delete运算符。使用delete方法不仅可以删除对象的属性,在这里也可以删除数组元素:

1 var a=[1,2,3];
2 delete a[1]; //a在索引1的位置不再有元素
3 alert(1 in a); //false,数组索引1并未在数组中定义
4 a.length; //3,delete操作符不会修改数组的length属性,也不会将元素从高索引处以下填充已删除属性留下的空白。

2.4 遍历数组

使用for语句遍历数组中的元素

1 Var arr=[1,2,3];
2 For(var i=0,len=arr.length;i<len;i++)
3 { //使用len=arr.length可以优化遍历的性能,不用每次都查询数组的长度
4 Console.log(arr[i]);
5 }

这里需要提到的是使用for…in循环枚举属性名,在遍历对象中属性时尤为重要。

3.ECMAScript 3 方法

3.1 添加元素

a) push()

该方法可向数组的末尾添加一个或是多个元素,并返回新的数组长度。可以这么来理解,push是对数据的压栈操作,栈不是什么很深的概念,不要被吓倒了,比如说,桌子上堆了厚厚的一摞书,压栈就是把书一本本的往上加,是不是很好理解。使用语法:arr.push(ele1,ele2…eleN);其中,第一个参数是必填的,指要添加到数组的第一个元素,后面的参数可选,在使用过程中也一般只传第一个参数,在做遍历即可。

b) unshift()

该方法可向数组的开头添加一个或多个元素,并返回新的长度。它的作用不要和push方法混淆,一个是从数组的开头进行元素添加,一个是从数组的末尾进行元素的添加。使用语法:arr.unshift(ele1,ele2…eleN);

c) concat()

该方法用于连接两个或是多个数组,注意的是,该方法不会改变现有的数组,只是会返回一个被连接后的数组。使用语法:arr.concat(arr1,arr2…arrN);

3.2 删除元素

a) pop()

该方法和push方法对应,但功能相反,用于删除并返回数组的最后一个元素,同时,数组的长度会被减1。如果数组已经为空,再去执行pop()方法,那么此时会返回undefined值。使用语法:arr.pop(),注意括号内不可传参数。

b) shift()

该方法和unshift方法对应,但功能相反,用于删除并返回数组的第一个元素。使用语法:arr.shift();

3.3 子数组处理

a) splice()

该方法功能很强大,可以用于对数组执行添加、删除和插入的功能,然后返回被删除的项目,该方法会改变原始的数组。

使用语法:arr.splice(index,count,item1,item2…itemN);

参数说明:index是必填的参数,而且必须是整数,这个参数指定了添加或是删除项目的位置,而且index为负数时,表明可以从数组结尾处规定的位置执行该方法;count也是必填参数,指定要删除的项目数量,如果设置为0,则不删除项目,后面添加参数的话,此时就可实现插入的功能了;item是可选的参数,如果不传,则指向删除的功能,传入的话,可以实现添加和插入的功能

具体范例如下:

1). 插入功能(只插入):splice(start, 0, args);//返回的是处理后的数组

2). 替换功能(删除和插入):splice(start, delCount, args);//返回的是处理后的数组

3). 删除功能(只删除):splice(start, delCount);//返回的是被删除的元素数组,若没有删除任何元素,则返回空数组

b) slice()

该方法可以从已有的数组中返回规定的元素,此方法返回的是一个新数组,包含start到end(注意,不包含该元素)的arrObj数组中的元素。

使用语法:arr.slice(start, end);

参数说明:参数start必填,它规定了开始选取元素的位置,但如果是负数的话,就是表示从数组的尾部开始算起的位置(-1为最后一个元素,-2为倒数第二个元素…);参数end是选的,它规定了从何处结束选取,如果不传,则表示从规定的起始位置一直选取到结束,如果是负数,表示从数组的尾部开始算起的元素。

以下为模拟slice方法的实现:

 1 /*------------------------
 2 
 3  *函数名称:
 4 
 5  *功能描述:slice的实现原理
 6 
 7  *思   路:遍历数组中的各项元素,利用push方法将取到的元素存放在新数组返回
 8 
 9  *-----------------------*/
10 
11 Array.prototype.slice=function (a,b)
12 
13 { //先判断参数b的类型,如果为数值则取b本身,如果不是,比如没有传参数b,则取执行该方法的数组长度赋值给b
14 
15 b==typeof b=='number'?b:this.length;
16 
17 var arr=[];
18 
19 for(var i=a;i<b;i++)
20 
21 { //以a为起始索引值,以b为结束索引值(但不包含b),将取到的元素放进arr
22 
23 arr.push(this[i]);
24 
25 }
26 
27 return arr; //将存放于arr数组中的元素返回
28 
29 };

3.4 排序方法

a) reverse()

该方法是用于颠倒数组中元素的顺序,需要注意的是,该方法直接在原数组上进行操作,会改变原数组,而不会创建一个新的数组,并且,使用时不需要传参,如:arr.reverse()。

b) sort()

该方法用于对数组的元素进行排序,和reverse方法类似,该方法也是直接在原数组上进行排序,会改变原数组。在这里,需要对该方法中的传参好好说明,调用该方法但不传参时,如arr.sort(),此时该方法将按ASCII码的字母顺序对数组中的元素进行排序,这是封装方法时这样设计的,使用时尤其应该注意。如果需要按其他的标准进行排序时,可以将函数直接量作为该方法的传参,从而实现排序,如以下写法可以实现升序排序:

arr.sort(function (n1, n2){ return n1-n2; });

以上是数组的两个简单排序方法的介绍,具体有关数组排序的算法及性能分析请参看后面第5部分内容。

3.5 数组转换

在开始了解这几个方法的时候,先普及一个重要的概念,即隐式调用和显式调用。其实这两个概念也很简单,所谓显式调用即是我们在编码的过程中主动的去调用该方法,而隐式调用则是js在处理过程中在进行某些数据类型转换时自动默认的调用该方法。那么为什么在这里提及隐式调用呢,由于toString()和valueOf()等方法属于原型对象都共有的方法,都会在需要的时候隐式的调用,完成相关的数据操作。

a) toString()

该方法用于将一个逻辑值转换成字符串,并返回结果,前提是调用该方法的对象是boolean类型,如果不是,则会抛出异常;该方法不需要传参,返回的是数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,这句话是不是有点绕了,请看以下代码理解:

1 var country=[‘China’, ‘Canada’,’France’];
2 
3 alert(country.toString()); //返回的值为:China, Canada,France

b) toLocaleString()

toLocaleString()方法用于将数组转换为本地字符串,作用和toString差不多,但此方法是使用地区特定的分隔符来将生成的字符串连接起来。

c) valueOf()

该方法可以返回Array对象的原数值,通常都是在后台隐式的调用该方法,一般不会显式的出现我们的代码中。

d) join()

该方法用于将一个数组的所有元素都按指定的分隔符分隔,转换成字符串。

e) split()

该方法用于将字符串按片段分隔创建数组,和join()的功能正好相反

4.ECMAScript 5 方法

4.1 位置方法

位置方法包括indexOf()和lastIndexOf()这两个方法用于搜索整个数组中具有给定值的元素,并且返回找到的第一个元素的索引值,如果没有找到,则返回-1。两者不同的是,indexOf方法是从头至尾的搜索,而lastIndexOf是从尾向前的搜索。

参数说明:indexOf()和lastIndexOf()方法的第一个参数都是必需的,传入的是需要搜索的目标值,而第二个参数是可选的,即指定开始搜索的位置,如果不传的话,indexOf()方法默认从头开始搜索,lastIndexOf方法默认从尾开始搜索。重要的是,第二个参数可以是一个负值,表示相对数组末尾的偏移量,所以这也使得以上两方法的使用没有特别明确的界限。

4.2 迭代方法

在学习理解这五个迭代方法之前,我们来先模拟一下forEach()这个方法的实现,以便理解这些方法中用到的传参(这几个方法的形式参数都是一样的),这对后续学习很有帮忙,我们需要从根处模拟这些方法是用什么样的思路来封装的,好了,请看代码:

 1 Array.prototype.forEach=function(fun,context){
 2 
 3 // forEach的实现原理
 4 
 5 var len=this.length;
 6 
 7 var context=arguments[1];     //即使为undefined,call函数也正常运行。
 8 
 9 if(typeof fun !=="function"){
10 
11 throw "输入正确函数!";
12 
13 }
14 
15 for(var i=0;i<len;i++){
16 
17 fun.call(context,this[i],i,this); //注意这四个参数,很关键,context是上下文,即作用对象,this[i]是各项元素的值,i为各项的索引值,this为执行该方法的主体
18 
19 }
20 
21 };
22 
23 arr.forEach(function(item,index,arr){
24 
25 console.log(item,index,arr);
26 
27 //item  arr的每一项的内容
28 
29 //index  各项的索引值
30 
31 //arr  即是输入的原数组值
32 
33 });

代码解析:审查以上代码,这是用原型的方法来模拟forEach的实现,forEach()方法中有两个传参,第一个fun(传入一个函数),第二个是context(上下文,可以理解为执行该方法的对象)。一般在使用过程中传入fun即可,在fun中自定义自己想要实现的功能。在fun中有三个传参,在代码中写的很清楚,请理解之。

a) every()

该方法对数组中的每一项都运行给定的函数直接量,如果该函数对每一项都返回true,则该方法返回true,注意是每一项都满足条件该方法才会返回true。

 1 var arr=[1,2,3,4,5,6,7];
 2 
 3 var result=arr.every(function (item, index, array){ return item>5; });
 4 
 5 alert(result); //此时会返回的结果是 false;
 6 
 7 var arr1=[3,4,5];
 8 
 9 var result1=arr1.every(function (item, index, array){ return item>2; });
10 
11 alert(result1); //此时会返回的结果是 true;

b) filter()

该方法对数组中的每一项都运行给定函数,返回该函数返回true的项组成的数组,从单词字面意思理解,该方法是对数据执行一个过滤的作用,满足条件的返回,不满足条件的丢弃,仅此而已,用个例子说明:

1 var arr=[1,2,3,4,5,6,7];
2 
3 var result=arr.filter(function (item, index, array){ return item>5; });
4 
5 alert(result); //此时会返回的结果是 [6,7];

c) forEach

对数组中的每一项运行给定函数,需要注意的是,这个方法没有返回值。在编码的过程中使用这个方法会带来很多便利,因为该方法就有一个遍历数组元素的作用,不用每次的写一个for循环来获取数组的每一项进行操作了。

d) map()

对数组中的每一项运行给定的函数,返回每次函数调用的结果组成的数组,注意哦,这里返回的可是数组,和some()等方法返回布尔值不一样,使用过程中需要区别。

e) some()

对数组中的每一项运行给定的函数,如果该函数对任一项都返回true,则该方法返回true,和every()方法有点相似,但是要区别啊亲,简单区别的话可以从单词下手,every()是传入的函数需要对每一项都需要满足条件,该方法次啊会返回true;而some()方法是只要传入的函数对数组中的某一项返回true,该方法就会返回true。

4.3 缩小方法

a) reduce()

该方法从数组的第一项开始逐个遍历至尾,使用指定的函数来将数组的元素进行整合,只生成单个的值,这就是缩小方法,很好理解吧。另外,需要对该方法的参数进行说明一下,reduce()需要两个参数,第一个参数是执行化简操作的函数,这个参数必需;第二个参数是一个传递给函数的初试值,这里需要理解一下,所谓初始值就是传给第一个函数参数执行操作的第一个值,在接下来的操作中,这个值就是上一次函数的返回值了,而当第二个传参不使用时,化简函数就使用数组的第一个元素和第二个元素作为其第一个和第二个参数进行计算。请结合以下代码理解:

1 var arr=[1,2,3,4,4,5,6,6];
2 
3 var sum=arr.reduce(function (x, y){ return x+y; }, 0); //数组元素求和
4 
5 var multi=arr.reduce(function (x, y){ return x*y}, 1); //求出数组中各元素的积
6 
7 var max=arr.reduce(function (x, y){ return (x>y)?x:y}); //求出最大值为6

b) reduceRight()

该方法的使用和reduce()是一样的,这里可以联想到indexOf()方法和lastIndexOf()方法的关系,即reduceRight()方法是按照数组索引从高到低的处理数组。

4.4 数组检测方法

检测数组是一个简单而又基本的方法,但正是因为基础,所以显得更重要,因为检测数组会渗透进代码的每个角落。在这里,学习了检测数组的方法后,要学会举一反三,比如,如何检测对象,如何检测原型,如何检测字符串等。理解并掌握后,会起到很重要的桥梁作用。

在没有isArray()方法之前,我们可以用instanceof操作符来判断一个对象是否属于Array类,但是instanceof也有局限性,比如它只在单一的全局执行环境可以起到检测的作用,一旦网页中出现多个框架,则会相应产生多个不同的全局作用域,也就会有不同版本的Array构造函数,此时的instanceof就无法完成检测的功能了。

封装设计好的isArray()方法可以解决以上论述的烦恼,是不是这个方法就可以实现完美的检测功能呢,js第二定律的作用再次发挥了-----好的东西是不可能兼容的。对于目前的浏览器现况,只有部分浏览器可以支持该方法,所以,咱们自己动手,一起写下以下代码:

1 function isArray(value){
2 
3 return object.prototype.toString.call(value) ==’[object Array]’;
4 
5 }

5.数组去重

5.1 数组内单一数据类型去重

  1 /*------------------------
  2 
  3  *函数名称:Array.prototype.distinct
  4 
  5  *功能描述:将只含有数值元素的数组去重
  6 
  7  *思   路:利用基本的排序思路去重,性能较低
  8 
  9  *-----------------------*/
 10 
 11 Array.prototype.distinct=function (){
 12 
 13 var a=this;
 14 
 15 for(var j=0;j<a.length-1;j++){
 16 
 17 var nItem=a[j];
 18 
 19 for(var i=j+1;i<a.length;){
 20 
 21 if(nItem==a[i]){
 22 
 23 a.splice(i,1);
 24 
 25 }else{
 26 
 27 i++;
 28 
 29 }
 30 
 31 }
 32 
 33 }
 34 
 35 return a;
 36 
 37 }
 38 
 39 /*------------------------
 40 
 41  *函数名称:Array.prototype.distinct
 42 
 43  *功能描述:牺牲空间换时间,利用自定义属性的方法去重
 44 
 45  *思   路:
 46 
 47  *-----------------------*/
 48 
 49 Array.prototype.distinct=function (){
 50 
 51 var obj={};
 52 
 53 for(var i=0;i<this.length;){
 54 
 55 if(obj[this[i]]!=this[i]){
 56 
 57 obj[this[i]]=this[i];
 58 
 59 i++;
 60 
 61 }else{
 62 
 63 this.splice(i,1);
 64 
 65 }
 66 
 67 }
 68 
 69 };
 70 
 71 /*------------------------
 72 
 73  *函数名称:
 74 
 75  *功能描述:forEach方法去重
 76 
 77  *思   路:
 78 
 79  *-----------------------*/
 80 
 81 Array.prototype.distinct=function (){
 82 
 83 var a=[],obj={},temp=this;
 84 
 85 temp.forEach(function (value, index, temp){
 86 
 87 if(!obj[typeof (value)+value]) {
 88 
 89 a.push(value);
 90 
 91 obj[typeof (value)+value]=true;
 92 
 93 }
 94 
 95 });
 96 
 97 return a;
 98 
 99 };
100 
101 /*------------------------
102 
103  *函数名称:singleTypeDistinct
104 
105  *功能描述:将数组内除
106 
107  *思   路:
108 
109  *-----------------------*/
110 
111 function typeDistinct (arr){
112 
113 var obj={},temp=[]; //temp用于存放去重后的元素
114 
115 for(var i=0;i<arr.length;i++){
116 
117 if(!obj[typeof (arr[i])+arr[i]]){
118 
119 temp.push(arr[i]);
120 
121 obj[typeof (arr[i])+arr[i]] =true;
122 
123 }
124 
125 }
126 
127 return temp;
128 
129 }

5.2 数组内复杂数据类型去重

 1 /*------------------------
 2 
 3  *函数名称:
 4 
 5  *功能描述:解决对象去重问题
 6 
 7  *思   路:
 8 
 9  *-----------------------*/
10 
11 function multiTypeDistinct (arr){
12 
13 //判断对象类型的方法
14 
15 function isEqual(obj1,obj2){
16 
17 //判断两个对象的地址是否一样,地址一样则必相等,这里只为了优化性能
18 
19 if(obj1===obj2){
20 
21 return true;
22 
23 }
24 
25 if(typeof(obj1)=="object"&&typeof(obj2)=="object"){
26 
27 //判断两个对象类型一致且为object类型
28 
29 var count=0;
30 
31 for(var attr in obj1){
32 
33 count++;
34 
35 if(!isEqual(obj1[attr],obj2[attr])){
36 
37 return false;
38 
39 }
40 
41 }
42 
43 for(var attr in obj2){
44 
45 count--;
46 
47 }
48 
49 return count==0;
50 
51 }else if(typeof(obj1)=="function"&&typeof(obj2)=="function"){
52 
53 //判断两个对象类型一致且为function类型
54 
55 if(obj1.toString()!==obj2.toString()){
56 
57 return false;
58 
59 }
60 
61 }else { //判断两个对象类型不一致,再判断值是否相等
62 
63 if(obj1!=obj2){
64 
65 return false;
66 
67 }
68 
69 }
70 
71 return true;
72 
73 };
74 
75 //temp作为传入数组arr的备份,在不改变原数组的基础上进行去重操作
76 
77 var temp=arr.slice(0);
78 
79 for(var i=0;i<temp.length;i++){
80 
81 for(j=i+1;j<temp.length;j++){
82 
83 if(isEqual(temp[j],temp[i])){
84 
85 temp.splice(j,1);//删除该元素
86 
87 j--;
88 
89 }
90 
91 }
92 
93 }
94 
95 return temp;
96 
97 }

5.3 类数组对象转化成数组去重

Arguments对象是函数体内的特殊对象,它是一个类数组对象,用于保存传入函数中的所有参数。重要的是,arguments对象有一个callee属性,这个属性是一个指针,指向拥有这个arguments对象的函数,即在函数体内使用arguments.callee可以实现函数的重载。

nodeList是DOM中所涉及的知识。可以这样理解,每个DOM节点都有childNodes属性,其中保存着nodeList对象。nodeList也是一个类数组对象,实时动态地保存着一组有序的节点。

那么为什么要在数组的部分讨论这两个对象呢,这不是在废话。因为arguments对象和nodeList对象都是类数组对象,它们虽然不是真正的数组,但是具有数组类型的特点。那么,在使用的时候,我们可以写一些方法,把这个类数组转化成真正的数组,然后调用数组相关的方法,从而去解决那么令人无奈而烦恼的问题,所以理解这类数组对象是很重要的。

下面讲类数组怎么转化成数组,再进行去重。

 1 /*------------------------
 2 
 3  *函数名称:listToArray()
 4 
 5  *功能描述:将nodeList类型转化为数组
 6 
 7  *思   路:
 8 
 9  *-----------------------*/
10 
11 function listToArray(eles){
12 
13 try{
14 
15 return Array.prototype.slice.call(eles,0)
16 
17 }catch(e){
18 
19 var a=[];
20 
21 for(var i=0;i<eles.length;i++)
22 
23 {
24 
25 a.push(eles[i]);
26 
27 }
28 
29 return a;
30 
31 }
32 
33 }

5.4字符串转化成数组去重

 1 /*------------------------
 2 
 3  *函数名称:stringDist
 4 
 5  *功能描述:字符串转换成数组去重
 6 
 7  *思   路:
 8 
 9  *-----------------------*/
10 
11 var str='777748908883859999';
12 
13 var arr=[].slice.call(str,0);
14 
15 function stringDist(arr){
16 
17 var obj={},a=[];
18 
19 for(var i=0,len=arr.length;i<len;i++){
20 
21 var temp=arr[i];
22 
23 if(!obj.hasOwnProperty(temp)){ //判断obj对象是否具有temp属性
24 
25 obj[temp]=1;
26 
27 a.push(temp);
28 
29 }else{
30 
31 obj[temp]++;
32 
33 }
34 
35 }
36 
37 return a;
38 
39 //return obj; //返回每一项及其重复的个数
40 
41 }
42 
43 console.log(stringDist(arr));

6.排序算法

什么是算法,它是为创造更优雅的程序而积累的古老的智慧。好的算法本身会成为程序设计的范本,学习算法也可为编写一个良好程序的过程中起到指导作用,提高编程的能力。学过算法的人写出的程序和没学过算法的人写出的程序有很明显的差距,要写出既能正确执行又能提高效率的好程序,算法学习是不可或缺的,所以,在这里当是抛砖引玉,在以后的编程过程中,大家要对算法提高关注力,在实现功能的时候去琢磨更高效的思路和方法,慢慢养成的这个习惯,会让你在编程乃至生活中都会起到一个润物细无声的作用,进而让你升华到另一个高度。

以下分为分为四个方面的排序算法进行详述,分别为基本的数组排序算法、表格排序、DOM对象的排序。

6.1 基本数组排序算法

  1 /*------------------------
  2 
  3  *函数名称:bubbleSort(arr)
  4 
  5  *功能描述:冒泡排序
  6 
  7  *思   路:冒泡排序是简单交换法的体现,即通过相邻数据大小进行排序。由于比较的次数较多,所花费的时间开销较大,性能也较低,在大数据排序时速度较慢,但优点是容易理解。
  8 
  9  *-----------------------*/
 10 
 11 function bubbleSort(arr){
 12 
 13 var temp=null; //创建一个临时交换变量
 14 
 15 for(var i=0;i<arr.length-1;i++) { //遍历数组内的元素,每一个元素都和其他元素比较
 16 
 17 for(var j=0;j<arr.length-i-1;j++){
 18 
 19 if(arr[j]> arr [j+1]){ //相邻两个元素进行比较
 20 
 21 temp=arr[j]; //如果前面元素大于后面元素,则两者交换位置,实现升序排序
 22 
 23 arr[j]=arr[j+1];
 24 
 25 arr[j+1]=temp;
 26 
 27 }
 28 
 29 }
 30 
 31 }
 32 
 33 return arr;
 34 
 35 }
 36 
 37 /*------------------------
 38 
 39  *函数名称:insertSort(arr)
 40 
 41  *功能描述:插入排序
 42 
 43  *思   路:插入排序的核心思想是将未排序部分的首位元素插入已排序部分中符合大小关系的正确位置,这样可减少循环比较次数,比冒泡排序性能较高
 44 
 45  *-----------------------*/
 46 
 47 function insertSort(arr){
 48 
 49 var temp=null;
 50 
 51 for(var i=1;i<arr.length;i++){
 52 
 53 temp=arr[i]; //将未排序数组的首位元素保存在临时变量,以便进行比较
 54 
 55 for(var j=i-1;(j>=0&&arr[j]>temp);j--){ //注意for循环里面的语句,这是从高位索引值的元素往低索引值的元素逐个比较,括号内的判断语句作用保证已排序数组内必须是有元素的,而且是按升序排序
 56 
 57 arr[j+1]=arr[j];
 58 
 59 arr[j]=temp; //如果满足判断的条件则交换元素位置
 60 
 61 }
 62 
 63 }
 64 
 65 return arr;
 66 
 67 }
 68 
 69 /*------------------------
 70 
 71  *函数名称:quickSort(arr)
 72 
 73  *功能描述:快速排序
 74 
 75  *思   路:快速排序是很快的排序算法,sort中就用到了快速排序思想进行设计封装。快速排序也可称为二分排序,重复根据基准值分为比基准值小的组合比基准值大的组进行排序,最大限度减少数值比较的次数,是性能较快的一种排序算法
 76 
 77  *-----------------------*/
 78 
 79 function quickSort(arr){
 80 
 81 if(arr.length <= 1){ //保证数组内有2到多个元素,否则不具备排序条件,直接结束
 82 
 83 return arr;
 84 
 85 }
 86 
 87 var p=Math.floor(arr.length/2); //取数组的中间索引值
 88 
 89 var mid=arr.splice(p,1); //在原数组中将此元素删除,并将中间索引值对应的元素返回赋值给mid变量,作为比较的基准值
 90 
 91 var left=[]; //定义一个存放比基准值小的元素的数组
 92 
 93 var right=[]; //定义一个存放比基准值大的元素的数组
 94 
 95 for(var i=0;i<arr.length;i++){
 96 
 97 if(arr[i]<mid){
 98 
 99 left.push(arr[i]); //将数组arr内比基准值小的元素存放于left数组
100 
101 }else{
102 
103 right.push(arr[i]); //将数组arr内比基准值大的元素存放于right数组
104 
105 }
106 
107 }
108 
109 return quickSort(left).concat(mid,quickSort(right)); //用concat方法将比较后的元素重组成一个新的数组,并递归调用函数本身,直至排序结束
110 
111 };

以上是几种基本常用的数组排序算法,这些思路也可移植到其他的代码实现上。当然,也还有许多其他的排序算法,比如桶排序、基数排序、希尔排序、归并排序以及利用堆结构进行排序的堆排序算法,这些思路可在学有余力时扩展,但务求掌握以上几种基本排序算法,力争举一反三。

6.2 DOM对象排序之表格排序

a) 利用sort()方法对数值排序

在学习对DOM排序之前,我们先利用sort()方法进行简单的排序,在第三节中我们知道,该方法默认是按ASCII码的字母顺序对数组中的元素进行排序,如果需要按其他的标准进行排序时,可以将函数直接量作为该方法的传参实现排序,如以下写法可以实现升序排序:

1 var arr=[3,4,2,7,5,9,1];
2 
3 arr.sort(function (n1, n2){ return n1-n2;});
4 
5 console.log(arr); //此时返回的arr为[1,2,3,4,5,7,9];

b) 利用sort()方法对数组内复杂数据类型排序

所谓数组内含有复杂数据类型是指数组内含有字符串或是对象等数据类型,那么此时应该如何利用该方法进行排序实现呢,请看以下代码:

1 var arr=[‘20px’, ‘300em’, ‘32px’];
2 
3 arr.sort(function (a, b){
4 
5 return parseInt(a)-parseInt(b);
6 
7 });

当然,这还只是初级的扩展数据类型进行排序,数组内含对象都数据类型的排序实现也可以在sort内定义相应的函数直接量。

c) 利用sort()方法对类数组对象排序

在本文的5.3节中,涉及到类数组对象转化成数组去重的简单方法。同样的,在进行DOM操作的时候,我们无可避免的会遇到对DOM对象的排序,那么怎么办呢,DOM对象可不是数组,数组的那些方法在它身上无法应用。那么,我们换个思路,将类似DOM对象这些类数组对象转换成数组,就可以利用数组的排序算法实现我们想要的排序目的了。以下利用表格排序的代码例子进行分析思路的拓展。

  1 //HTML部分的代码
  2 
  3 <input id="btn1" type="button" value="排序" />
  4 
  5 <table id="tab1" border="1" width="500">
  6 
  7 <thead>
  8 
  9      <td>Id</td>
 10 
 11      <td>Name</td>
 12 
 13      <td>Age</td>  
 14 
 15         <td>操作</td>
 16 
 17     </thead>
 18 
 19 <tbody>
 20 
 21      <tr>
 22 
 23          <td>1</td>
 24 
 25          <td>guo</td>
 26 
 27          <td>24</td>
 28 
 29             <td></td>
 30 
 31         </tr>
 32 
 33         <tr>
 34 
 35          <td>3</td>
 36 
 37          <td>feng</td>
 38 
 39          <td>24</td>
 40 
 41             <td></td>
 42 
 43         </tr>
 44 
 45         <tr>
 46 
 47          <td>5</td>
 48 
 49          <td>feng</td>
 50 
 51          <td>24</td>
 52 
 53             <td></td>
 54 
 55         </tr>
 56 
 57      <tr>
 58 
 59          <td>2</td>
 60 
 61          <td>yong</td>
 62 
 63          <td>24</td>
 64 
 65             <td></td>
 66 
 67         </tr>
 68 
 69      <tr>
 70 
 71          <td>4</td>
 72 
 73          <td>feng</td>
 74 
 75          <td>24</td>
 76 
 77             <td></td>
 78 
 79         </tr>
 80 
 81     </tbody>
 82 
 83 </table>
 84 
 85 //JS部分的代码
 86 
 87 window.onload=function ()
 88 
 89 { //获取点击排序的按钮和表格
 90 
 91 var oBtn=document.getElementById('btn1');
 92 
 93 var oTab=document.getElementById('tab1');
 94 
 95 //给按钮加点击的动作,点击后实现排序的效果
 96 
 97 oBtn.onclick=function (){
 98 
 99 var arr=[]; //定义一个数组
100 
101 for(var i=0;i<oTab.tBodies[0].rows.length;i++){
102 
103 arr[i]=oTab.tBodies[0].rows[i]; //取到表格中的每一行,存放于数组arr中
104 
105 }
106 
107 arr.sort( function (tr1, tr2){ //调用数组sort方法,在方法内定义排序的函数,关键处之一
108 
109 var n1=parseInt(tr1.cells[0].innerHTML); //每一行的第一格里的内容,存放的是id值,利用id值进行表格排序,这是核心,简单而又关键的地方
110 
111 var n2=parseInt(tr2.cells[0].innerHTML);
112 
113 return n1-n2;
114 
115 });
116 
117 for(var i=0;i<arr.length;i++){ //现在arr数组中存放的是已经按id排序后的每一行
118 
119 oTab.tBodies[0].appendChild(arr[i]); //将arr中的每一项进行DOM操作,插入表格中,这样就生成了排序之后的表格了
120 
121 }
122 
123 };
124 
125 };

7.数组克隆

 1 /*------------------------
 2 
 3  *函数名称:
 4 
 5  *功能描述:数组克隆的方法
 6 
 7  *思   路:
 8 
 9  *-----------------------*/
10 
11 Array.prototype.clone=function (){
12 
13 //return this.slice(0);
14 
15 return this.concat();
16 
17 };
18 
19 Array.prototype.clone=function (){
20 
21 var a=[];
22 
23 for(var i=0;i<this.length;i++){
24 
25 a.push(this[i]);
26 
27 }
28 
29 return a;
30 
31 };;

 

posted @ 2014-08-04 14:15  郭永峰  阅读(262)  评论(0编辑  收藏  举报