论坛首页 Web前端技术论坛

JavaScript内核系列 第5章 数组

浏览 19244 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-04-25  
hyj1254 写道
介绍下sort()函数的用法吧(这个问的有点弱了

关于数组的排序部分已经更新,可以看看,谢谢你的提醒!
0 请登录后投票
   发表时间:2010-04-25  
引用
另一个与其他语言的数组不同的是,字符串也可以作为数组的下标(事实上

Array也是Object类型,如果本文要介绍Array,就不应该把与Object相关的内容在这里介绍。
如果说给一个Object添加的property能被称为“下标”,如var a=[1,2];a.new="NEW";那么无法解释a.new不能参与到length的计算,不能被a.sort()操作等。

另外不要用for...in遍历数组对象,如果把它当作数组来用,一定要使用for(var i=0;i<a.length;i++)这样的方式,确保得到的每个“元素”都是这个数组的下标。

实际应用中,很少会把数组设计的离散。如果过于离散,就失去了数组的意义,莫不如使用Object容易理解。
0 请登录后投票
   发表时间:2010-04-26  
写的好极了。
0 请登录后投票
   发表时间:2010-04-26  
不错,很好的东西!
0 请登录后投票
   发表时间:2010-04-26  
楼主  讲的很不错 支持一下
0 请登录后投票
   发表时间:2010-04-26  
abruzzi 写道

第五章 数组

JavaScript的数组也是一个比较有意思的主题,虽然名为数组(Array),但是根据数组对象上的方法来看,更像是将很多东西混在在一起的结果。而传统的程序设计语言如C/Java中,数组内的元素需要具有相同的数据类型,而作为弱类型的JavaScript,则没有这个限制,事实上,JavaScript的同一个数组中,可以有各种完全不同类型的元素。

 

方法

描述

concat()  

连接两个或更多的数组,并返回结果。

join()

把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。

pop()

删除并返回数组的最后一个元素。

push()

向数组的末尾添加一个或更多元素,并返回新的长度。

reverse()

颠倒数组中元素的顺序。

shift()

删除并返回数组的第一个元素。

slice()

从某个已有的数组返回选定的元素。

sort()

对数组的元素进行排序。

splice()

删除元素,并向数组添加新元素。

unshift() 

向数组的开头添加一个或更多元素,并返回新的长度。

valueOf()

返回数组对象的原始值。

 

可以看出,JavaScript的数组对象比较复杂,包含有pop,push等类似与栈的操作,又有slice reversesort这样类似与列表的操作。或许正因为如此,JavaScript中的数组的功能非常强大。

5.1数组的特性

数组包括一些属性和方法,其最常用的属性则为lengthlength表示数组的当前长度,与其他语言不同的是,这个变量并非只读属性,比如:

 

var array = new Array(1, 2, 3, 4, 5); print(array.length); array.length = 3; print(array.length);


 

 

运行结果为:

 

 

5
3
1,2,3

 

注意到最后的print语句的结果是”1,2,3”,原因是对length属性的修改会使得数组后边的元素变得不可用(如果修改后的length比数组实际的长度小的话),所以可以通过设置length属性来将数组元素裁减。

另一个与其他语言的数组不同的是,字符串也可以作为数组的下标(事实上,在JavaScript的数组中,数字下标最终会被解释器转化为字符串,也就是说,所谓的数字下标只不过是看着像数字而实际上是字符的属性名),比如:

 

var stack = new Array(); stack['first'] = 3.1415926; stack['second'] = "okay then."; stack['third'] = new Date(); for(var item in stack){ print(typeof stack[item]); }


 

 

运行结果为:


 

number
string
object

 

在这个例子里,还可以看到不同类型的数据是如何存储在同一个数组中的,这么做有一定的好处,但是在某些场合则可能形成不便,比如我们在函数一章中讨论过的sum函数,sum接受非显式的参数列表,使用这个函数,需要调用者必须为sum提供数字型的列表(当然,字符串无法做sum操作)。如果是强类型语言,则对sum传入字符串数组会被编译程序认为是非法的,而在JavaScript中,程序需要在运行时才能侦测到这一错误。

5.2使用数组

5.2.1 数组的基本方法使用

数组有这样几种方式来创建:

var array = new Array(); var array = new Array(10);//长度 var array = new Array("apple", "borland", "cisco");


 

 

不过,运用最多的为字面量方式来创建,如果第三章中的JSON那样,我们完全可以这样创建数组:

 

var array = []; var array = ["one", "two", "three", "four"];


 

 

下面我们通过一些实际的小例子来说明数组的使用(主要方法的使用)

向数组中添加元素:

 

var array = []; array.push(1); array.push(2); array.push(3); array.push("four"); array.push("five"); array.push(3.1415926);


 

 

 前面提到过,JavaScript的数组有列表的性质,因此可以向其中push不同类型的元素,接上例:

 

var len = array.length; for(var i = 0; i < len; i++){ print(typeof array[i]); }


 

 

结果为:


 

number
number
number
string
string
number

 

弹出数组中的元素:

 

for(var i = 0; i < len; i++){ print(array.pop()); } print(array.length);


 

 

运行结果如下,注意最后一个0是指array的长度为0,因为这时数组的内容已经全部弹出:


 

3.1415926
five
four
3
2
1
0

 

join,连接数组元素为一个字符串:


 

array = ["one", "two", "three", "four", "five"]; var str1 = array.join(","); var str2 = array.join("|"); print(str1); print(str2);

 

运行结果如下:


 

one,two,three,four,five
one|two|three|four|five

 

连接多个数组为一个数组:

 

var another = ["this", "is", "another", "array"]; var another2 = ["yet", "another", "array"]; var bigArray = array.concat(another, another2);


 

 

结果为:


 

one,two,three,four,five,this,is,another,array,yet,another,array

 

从数组中取出一定数量的元素,不影响数组本身:


 

print(bigArray.slice(5,9));

 

结果为:

 

this,is,another,array

 


slice方法的第一个参数为起始位置,第二个参数为终止位置,操作不影响数组本身。下面我们来看splice方法,虽然这两个方法的拼写非常相似,但是功用则完全不同,事实上,splice是一个相当难用的方法:

 

 

bigArray.splice(5, 2); bigArray.splice(5, 0, "very", "new", "item", "here");

 

第一行代码表示,从bigArray数组中,从第5个元素起,删除2个元素;而第二行代码表示,从第5个元素起,删除0个元素,并把随后的所有参数插入到从第5个开始的位置,则操作结果为:

one,two,three,four,five,very,new,item,here,another,array,yet,another,array

 

我们再来讨论下数组的排序,JavaScript的数组的排序函数sort将数组按字母顺序排序,排序过程会影响源数组,比如:

 

var array = ["Cisio", "Borland", "Apple", "Dell"]; print(array); array.sort(); print(array);


 

 

执行结果为:

 

 

Cisio,Borland,Apple,Dell
Apple,Borland,Cisio,Dell

 

这种字母序的排序方式会造成一些非你所预期的小bug,比如:

 

var array = [10, 23, 44, 58, 106, 235]; array.sort(); print(array);


 

 

得到的结果为:

 

 

10,106,23,235,44,58

 

可以看到,sort不关注数组中的内容是数字还是字母,它仅仅是按照字母的字典序来进行排序,对于这种情况,JavaScript提供了另一种途径,通过给sort函数传递一个函数对象,按照这个函数提供的规则对数组进行排序。

 

function sorter(a, b){ return a - b; } var array = [10, 23, 44, 58, 106, 235]; array.sort(sorter); print(array);


 

 

函数sorter接受两个参数,返回一个数值,如果这个值大于0,则说明第一个参数大于第二个参数,如果返回值为0,说明两个参数相等,返回值小于0,则第一个参数小于第二个参数,sort根据这个返回值来进行最终的排序:

 

 

10,23,44,58,106,235

 

当然,也可以简写成这样:


 

array.sort(function(a, b){return a - b;});//正序 array.sort(function(a, b){return b - a;});//逆序

 

5.2.2 删除数组元素

虽然令人费解,但是JavaScript的数组对象上确实没有一个叫做delete或者remove的方法,这就使得我们需要自己扩展其数组对象。一般来说,我们可以扩展JavaScript解释器环境中内置的对象,这种方式的好处在于,扩展之后的对象可以适用于其后的任意场景,而不用每次都显式的声明。而这种做法的坏处在于,修改了内置对象,则可能产生一些难以预料的错误,比如遍历数组实例的时候,可能会产生令人费解的异常。

数组中的每个元素都是一个对象,那么,我们可以使用delete来删除元素吗?来看看下边这个小例子:

 

var array = ["one", "two","three","four"]; //数组中现在的内容为: //one,two,three,four //array.length == 4 delete array[2];


 

 

然后,我们再来看看这个数组的内容:

 

one, two, undefined, four
//array.length == 4
 

可以看到,delete只是将数组array的第三个位置上的元素删掉了,可是数组的长度没有改变,显然这个不是我们想要的结果,不过我们可以借助数组对象自身的slice方法来做到。一个比较好的实现,是来自于jQuery的设计者John Resig

 

//Array Remove - By John Resig (MIT Licensed) Array.prototype.remove = function(from, to) { var rest = this.slice((to || from) + 1 || this.length); this.length = from < 0 ? this.length + from : from; return this.push.apply(this, rest); };

 

这个函数扩展了JavaScript的内置对象Array,这样,我们以后的所有声明的数组都会自动的拥有remove能力,我们来看看这个方法的用法:

 

var array = ["one", "two", "three", "four", "five", "six"]; print(array); array.remove(0);//删除第一个元素 print(array); array.remove(-1);//删除倒数第一个元素 print(array); array.remove(0,2);//删除数组中下标为0-2的元素(3个) print(array);


 

 

会得到这样的结果:

 

one,two,three,four,five,six
two,three,four,five,six
two,three,four,five
five

 

也就是说,remove接受两个参数,第一个参数为起始下标,第二个参数为结束下标,其中第二个参数可以忽略,这种情况下会删除指定下标的元素。当然,不是每个人都希望影响整个原型链(原因在下一个小节里讨论),因此可以考虑另一种方式:

 

//Array Remove - By John Resig (MIT Licensed) Array.remove = function(array, from, to) { var rest = array.slice((to || from) + 1 || array.length); array.length = from < 0 ? array.length + from : from; return array.push.apply(array, rest); };

 

其操作方式与前者并无二致,但是不影响全局对象,代价是你需要显式的传递需要操作的数组作为第一个参数:

 

var array = ["one", "two", "three", "four", "five", "six"]; Array.remove(array, 0, 2);//删除0, 1, 2三个元素 print(array);


 

 

这种方式,相当于给JavaScript内置的Array添加了一个静态方法。

5.2.3遍历数组

在对象与JSON这一章中,我们讨论了for…in这种遍历对象的方式,这种方式同样适用于数组,比如:

 

var array = [1, 2, 3, 4]; for(var item in array){ print(array[item]); }


 

 

将会打印:

 

1
2
3
4

 

但是这种方式并不总是有效,比如我们扩展了内置对象Array,如下:

 

Array.prototype.useless = function(){}

 

然后重复执行上边的代码,会得到这样的输出:

 

1
2
3
4
function(){}

 

设想这样一种情况,如果你对数组的遍历做sum操作,那么会得到一个莫名其妙的错误,毕竟函数对象不能做求和操作。幸运的是,我们可以用另一种遍历方式来取得正确的结果:

 

for(var i = 0, len = array.length; i < len;i++){ print(array[i]); }


 

 

这种for循环如其他很多语言中的写法一致,重要的是,它不会访问哪些下标不是数字的元素,如上例中的function,这个function的下标为useless,是一个字符串。从这个例子我们可以看出,除非必要,尽量不要对全局对象进行扩展,因为对全局对象的扩展会造成所有继承链上都带上“烙印”,而有时候这些烙印会成为滋生bug的温床。

 

附:由于作者本身水平有限,文中难免有纰漏错误等,或者语言本身有不妥当之处,欢迎及时指正,提出建议,参与讨论,谢谢大家!

 

0 请登录后投票
   发表时间:2010-04-26  
发现数组是数组、链表、栈、HashMap的结合体
0 请登录后投票
   发表时间:2010-04-26  
accphc 写道
发现数组是数组、链表、栈、HashMap的结合体

对,它就是这么一结合体。所以要单独开一章出来讨论。如果像传统的语言中,数组作为一个内嵌的数据结构存在的话,就简单多了,只需要说“JavaScript数组跟其他语言中的数组类似”即可,呵呵。
0 请登录后投票
   发表时间:2010-04-26  
楼主不要操之过急 慢工出细活 出一章就要精一章 保质保量才是王道
0 请登录后投票
   发表时间:2010-04-26  
jwtiger 写道
楼主不要操之过急 慢工出细活 出一章就要精一章 保质保量才是王道

嗯,谢谢你的提醒。这个系列最初是在09年十二月左右开始的,有些章节一气呵成,有的则需要更深入的研究,消化才慢慢加进来,正是基于“尽量的保证质量”这个想法才发的很慢,呵呵。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics