JavaScript 的数组也是一个比较有意思的主题,虽然名为数组(Array),但是根据数组对象上的方法来看,更像是将很多东西混在在一起的结果。而传统的程序设计语言如 C/Java 中,数组内的元素需要具有相同的数据类型,而作为弱类型的 JavaScript,则没有这个限制,事实上,JavaScript 的同一个数组中,可以有各种完全不同类型的元素。
方法 | 描述 |
---|---|
concat() | 连接两个或更多的数组,并返回结果。 |
join() | 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。 |
pop() | 删除并返回数组的最后一个元素。 |
push() | 向数组的末尾添加一个或更多元素,并返回新的长度。 |
reverse() | 颠倒数组中元素的顺序。 |
shift() | 删除并返回数组的第一个元素。 |
slice() | 从某个已有的数组返回选定的元素。 |
sort() | 对数组的元素进行排序。 |
splice() | 删除元素,并向数组添加新元素。 |
unshift() | 向数组的开头添加一个或更多元素,并返回新的长度。 |
valueOf() | 返回数组对象的原始值。 |
可以看出,JavaScript 的数组对象比较复杂,包含有 pop,push 等类似与栈的操作,又有 slice, reverse,sort 这样类似与列表的操作。或许正因为如此,JavaScript 中的数组的功能非常强大。
数组的特性
数组包括一些属性和方法,其最常用的属性则为 length,length 表示数组的当前长度,与其他语言不同的是,这个变量并非只读属性,比如:
var array = new Array(1, 2, 3, 4, 5);
print(array.length);
array.length = 3;
print(array.length);<span style="font-size: small;"><span style="font-size: 13px;">
</span></span>
运行结果为:
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 中,程序需要在运行时才能侦测到这一错误。
使用数组
数组的基本方法使用
数组有这样几种方式来创建:
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;});//逆序
删除数组元素
虽然令人费解,但是 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 接受两个参数,第一个参数为起始下标,第二个参数为结束下标,其中第二个参数可以忽略,这种情况下会删除指定下标的元素。当然,不是每个人都希望影响整个原型链(原因在下一个小节里讨论),因此可以考虑另一种方式: