数组元素的读和写
在读和写数组时,可以使用负数或非整数来索引数组,这种情况下,数值转换为字符串,字符串作为属性名。既然名字不是非负整数,它就只能当作常规的对象属性,而非数组的索引。同样,如果凑巧使用的是非负整数的字符串,它就当作数组索引,而非对象属性。当使用的一个浮点数和一个整数相等时情况也是一样:
a[-1.23] = true //创建一个名为"-1.23"的属性
a["1000"] = 0; //这是数组的第1001个元素
a[1.000] //和a[1]相等
稀疏数组
足够稀疏的数组通常在实现上比稠密的数组更慢、内存利用率更高,在这样的数组中查找元素的时间与常规对象属性的查找时间一样长。
注意,当在数组直接量中省略时不会创建稀疏数组。省略的元素在数组中是存在的,其值为undefined。这和数组元素根本不存在是有一些微妙区别的。可以用in操作符检测两者之间的区别:
var a1 = [,,,]; //数组是[undefined,undefined,undefined]
var a2 = new Array(3); //该数组根本没有元素
0 in a1 //=> true: a1在索引0处有一个元素
0 in a2 //=> false: a2在索引0处没有一个元素
数组长度
-
如果为一个数组元素赋值,它的索引i大于或等于现有数组的长度时,length属性的值将设置为i+1。
-
当设置length属性为一个小于当前长度的非负整数n时,当前数组中那些索引值大于或等于n的元素将从中删除。
-
将数组的length属性值设置为大于其当前的长度。实际上这不会向数组中添加新的元素,它只是在数组尾部创建一个空的区域。
a = [1,2,3,4,5];
a.length = 3; //现在a为[1,2,3]
a.length = 0; //删除所有的元素,a为[]
a.length = 5; //长度为5,但是没有元素,就像new Array(5)
删除数组元素
删除数组元素与为其赋undefined值是类似的(但有一些微妙的区别)。注意对一个数组元素使用delete不会修改数组的length属性,也不会将元素从高索引处移下来填充已删除属性留下来的空白。如果从数组中删除一个元素,它就变成稀疏数组。
数组遍历需要注意for/in
通常数组元素的遍历实现是升序的。但不能保证一定是这样的。特别地,如果数组同时拥有对象属性和数组元素,返回的属性名很可能是按照创建的顺序而非数值的大小顺序。如何处理这个问题的实现各不相同,如果算法依赖于遍历的顺序,那么最好不要使用for/in而用常规的for循环。
数组方法
Array.join()方法将数组中所有元素都将转化为字符串并连接在一起,返回最后生成的字符串:
var a = [1,2,3]; //创建一个包含三个元素的数组
a.join(); //=>"1,2,3"
a.join(" "); //=>"1 2 3"
a.join(""); //=>"123"
var b = new Array(10); //长度为10的空数组
b.join("-"); //=>"----------"
Array.reverse()方法将数组中的元素颠覆顺序,返回逆序的数组。它采取了替换;换句话说,它不通过重新排列的元素创建新的数组,而是在原先的数组中重新排列它们:
var a = [1,2,3];
a.reverse().join() //=>"3,2,1",并且现在的a是[3,2,1]
sort()
Array.sort()方法将数组中的元素排序并返回排序后的数组。当不带参数嗲用sort()时,数组元素以字母表排序(如有必要将临时转换为字符串进行比较):
var a = new Array("banana","cherry","apple");
a.sort();
var s = a.join(","); //s=="apple,banana,cherry"
如果数组包含undefined元素,会被排到数组的尾部。
假设第一个参数应该在前,比较函数应该返回一个小于0的数值。反之,假设第一个参数应该在后,函数应该返回一个大于0的数值。并且,假设两个值相等,函数应该返回0:
var a = [33,4,1111,222];
a.sort(); //字母表顺序:1111,222,33,4
a.sort(function(a,b){
return a-b //数值顺序:4,33,222,1111
});
a.sort(funtion(a,b){ return b-a }); //数值大小相反的顺序
a = ['ant','Bug','cat','Dog'];
a.sort() //区分大小写的排序:["Bug","Dog","ant","cat"]
a.sort(function(s,t){ //不区分大小写的排序
var a = s.toLowerCase();
var b = s.toLowerCase();
if(a<b) retrun -1;
if(b>a) retrun 1;
return 0;
}); // => ["ant","Bug","cat","Dog"]
concat()
Array.concat()方法创建并返回一个新数组,它的元素包括调用concat()的原始数组和concat()的每个元素。
var a = [1,2,3];
a.concat(4,5); //返回[1,2,3,4,5]
a.concat([4,5]); //返回[1,2,3,4,5]
a.concat([4,5],[6,7]); //返回[1,2,3,4,5,6,7]
a.concat(4,[5,[6,7]]); //返回[1,2,3,4,5,[6,7]]
slice()
Array.slice()方法返回指定数组的一个片段或子数组。它的两个参数分别指定了片段的开始位置(包括第一个)和结束的位置(不包含最后一个):
var a = [1,2,3,4,5];
a.slice(0,3); //返回[1,2,3]
a.slice(3); //返回[4,5]
a.slice(1,-1); //返回[2,3,4]
a.slice(-3,-2); //返回[3]
splice()
Array.splice()方法是在数组中插入或删除元素的通用方法。它会修改调用数组。splice()和slice()拥有非常相似的名字,但它们的功能却有本质的区别。
splice()能够从数组中删除元素、插入元素到数组中或者同时完成这两种操作。第一个参数指定了插入和(或)删除的起始位置。第二个参数指定了应该从数组中删除的元素的个数。如果省略第二个参数,从起始点开始到数组结尾的所有元素都将被删除。splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组:
var a = [1,2,3,4,5,6,7,8];
a.splice(4); //返回[5,6,7,8];a是[1,2,3,4]
a.splice(1,2); //返回[2,3];a是[1,4]
a.splice(1,1); //返回[4];a是[1]
在两个参数之后,任意个数的参数指定了需要插入到数组中的元素:
var a = [1,2,3,4,5];
a.splice(2,0,'a','b'); //返回[];a是[1,2,'a','b',3,4,5]
a.splice(2,2,[1,2],3); //返回['a','b']; a是[1,2,[1,2],3,3,4,5]
注意,区别于concat(),splice()会插入数组本身而非数组的元素
push()和pop()
push()方法在数组的尾部添加一个或多个元素,并返回数组新的长度。pop()方法则相反:它删除数组的最后一个元素,减小数组长度并返回它删除的值。它们都修改并替换原始数组而非生成一个新的数组。
var stack = [];
stack.push(1,2); //stack:[1,2] 返回2
stack.pop(); //stack:[1] 返回2
stack.push([4,5]) //stack:[1,[4,5]] 返回2
statck.pop(); //stack:[1] 返回[4,5]
unshift()和shift()
unshift()和shift()方法与push()和pop()非常类似,前者只不过在头部操作
var a =[];
a.unshift(1); //a:[1] 返回:1
a.unshift(22); //a:[22,1] 返回:2
a.shift(); //a:[1] 返回:22
a.unshift(3,[4,5]); //a:[3,[4,5],1] 返回:3
a.shift();
a.shift();
a.shift(); //a:[] 返回:1
注意,当使用多个参数调用unshift()时,参数是一次性插入的而非一次一个插入。
forEach()
forEach()方法从头到尾遍历数组,为每个元素调用指定的函数。使用三个参数调用该数组:数组元素、元素的索引和数组本身:
var data = [1,2,3,4,5];
//计算数组的和值
var sum = 0;
data.forEach(function(value){ sum+=value });
sum //=>15
//每个数组元素的值自加1
data.forEach((v,i,a){ a[i]=v+1; });
data //=>[2,3,4,5,6]
注意,forEach()无法在所有元素都传递给调用的函数之前终止遍历。也就是说,没有像for循环中使用的相应的break语句。如果要提前终止,必须放在一个try块中,并能抛出一个异常。如果forEach()调用的函数抛出foreach.break异常,循环会提前终止:
function foreach(a,f,t){
try{ a.forEach(f,t); }
catch(e){
if (e === foreach.break) return;
else throw e;
}
}
foreach.break = new Error("StopIteration");
map()
map()方法调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。例如:
a = [1,2,3];
b = a.map(function(x) { return x*x }); //b是[1,4,9]
filter()
filter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的:该函数返回true或false:
a = [5,4,3,2,1];
smallvalues = a.filter(function(x) { return x<3 }); //[2,1]
everyother = a.filter(function(x,i) { retrun i%2==0 }); //[5,3,1]
注意,filter()会跳过稀疏数组中缺少元素,它的返回数组总是稠密的。为了压缩稀疏数组的空缺:
var dense = sparse.filter(function(x){ return x<3 }); //[2,1]
压缩空缺并删除undefined和null元素,可以使用filter():
a = a.filter(function(x) { return x !== undefined && x != null; })
every()和some()
every()和some()方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回true或false。every()方法就像数学中的"针对所有"的量词:当且仅当针对数组中所有元素调用判定函数返回true,它才返回true:
a = [1,2,3,4,5];
a.every(function(x) { return x<10; }) //=>true:所有的值<10
a.every(function(x) { return x%2 === 0 }) //=>false 不是所有的值都是偶数
some()方法就像数学中的"存在"量词:当数组中至少有一个元素调用判断函数返回true,它就返回true,但如果判断函数一直返回false,它将会遍历整个数组。
a = [1,2,3,4,5];
a.some(function(x) { return x%2===0 }) //=>true,a含有偶数
a.some(isNaN); //=>false,a不包含非数值元素
reduce()和reduceRight()
reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。
var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y) { return x+y },0); //数组求和
var produc = a.reduce(function(x,y) { return x*y },1) //数组求积
var max = a.reduce(function(x,y) { return (x>y)?x:y }) // 求最大值
当不指定初始值调用reduce时,它将使用数组的第一个元素作为其初始值。数组只有一个元素并且没有指定初始值,或者有一个空数组并且指定一个初始值——reduce()只是简单返回那个值不会调用函数。
reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低(从右到左)处理数组,而不是从低到高:
var a = [2,3,4];
//计算2^(3^4).乘方操作的优先顺序是从右到左
var big = a.reduceRight(function(accmulator,value){
return Math.pow(value,accumulator);
});
indexOf()和lastindexOf()
indexOf()和lastindexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1.indexOf()从头到尾搜索,而lastIndexOf()则方向搜索:
a = [0,1,2,1,0];
a.indexOf(1) //=>1:a[1]是1
a.lastindexOf(1) //=>3:a[3]是1
a.indexOf(3) //=>-1:没有值为3的元素
如下函数在一个数组中搜索指定的值并返回包含所有匹配的数组索引的一个数组,它展示了如何运用indexOf()的第二个参数来查找除了第一个以外匹配的值:
//在数组中查找所有出现的x,并返回一个包含匹配索引的数组
function findall(a,x){
var results = [], //将会返回的数组长度
len = a.length, //待搜索数组的长度
pos = 0; //开始搜索的位置
while(pos < len){
pos = a.indexOf(x,pos); //搜索
if(pos === -1) break; //未找到,就完成搜索
results.push(pos); //否则,在数组中存储索引
pos = pos + 1; //并从下一个位置开始搜索
}
return results //返回包含索引的数组
}
数组类型
可以使用Array.isArray()函数来判定它是否为数组:
Array.isArray([]) //=>true
Array.isArray({}) //=>false
isArray()的函数代码可以这样写:
var isArray = Function.isArray || function(o){
return typeof o === "object" &&
object.prototype.toString.call(o) === "[object Array]";
}
类数组对象
如下代码为一个常规对象增加了一些属性使其变成类数组对象,然后遍历生成的伪数组的"元素":
var a = {}; //从一个常规对象开始
//添加一些属性,称为"类数组"
var i = 0;
while(i<10){
a[i] = i*i;
i++;
}
a.length = i;
//现在,当做真正的数组遍历它
var total = 0;
for(var j = 0;j<a.length;j++){
total +=a[j];
}
下面的函数可以用来检测类数组对象:
//判定o是否是一个数组对象
//字符串和函数有length属性,但是它们
//可以使用typeof检测将其排除。在客户端JavaScript中,DOM文本节点
//也有length属性,需要用额外判断o.nodeType != 3将其排除
function isArrayLike(o){
if( o && //o非null、undefined等
typeof o === "object"&& //o是对象
isFinite(o.length) && //o.length是有限数值
o.length >= 0 && //o.length为非负数
o.length===Math.floor(o.length) && //o.length是整数
o.length < 4294967296) //o是数组对象
return true;
else
return false; //否则它不是
}
虽然类数组对象没有继承自Array.prototype,但可以间接地使用Function.call方法调用:
var a = {"0":"a","1":"b","2":"c",length:3}; //类数组对象
Array.prototype.join.call(a,"+"); //=> "a+b+c"
Array.prototype.slice.call(a,"+"); //=>["a","b","c"]:真正的数组副本
Array.prototype.map.call(a,function(x){
return x.toUpperCase();
}); //=>["A","B","C"]
作为数组的字符串
下面是访问单个字符的方法
var s = test;
s.charAr(0) //=>"t"
s[1] //=>"e"
数组的方法用到字符串上,如:
s = "JacaScript";
Array.prototype.join.call(s," "); //=>"J a v a S c r i p t"
Array.prototype.filter.call(s,
function(x){
return x.match(/[^aeiou]/) //只匹配非元音字母
}
).join("") //=>"JvScript"