1.数组类型
我们到处都可以看见数组是具有特殊行为的对象。给定一个未知的对象,判定它是否为数组通常非常
有用。在ECMAScript 5中,可以使用Array.isArray()函数来做这件事情:
Array.isArray([])//=>true
Array.isArray({})//=>false
但是,在ECMAScript 5以前,要区分数组和非数组对象却很困难。instanceof操作符只能用于简单的情形:
[]instanceof Array//=>true
({})instanceof Array//=>false
解决方案是检查对象的类属性。对数组而言该属性的值总是"Array",因此在ECMAScript 3中
isArray()函数的代码可以这样书写:
var isArray=Function.isArray||function(o){
return typeof o==="object"&&
Object.prototype.toString.call(o)==="[object Array]";
};
实际上,此处类属性的检测就是ECMAScript 5中Array.isArray()函数所做的事情。获得对象类属
性的技术使用了Object.prototype.toString()方法。
2.类数组对象
我们已经看到,JavaScript数组的有一些特性是其他对象所没有的:
1·当有新的元素添加到列表中时,自动更新length属性。
2·设置length为一个较小值将截断数组。
3·从Array.prototype中继承一些有用的方法。
4·其类属性为"Array"。
这些特性让JavaScript数组和常规的对象有明显的区别。但是它们不是定义数组的本质特性。一
种常常完全合理的看法把拥有一个数值length属性和对应非负整数属性的对象看做一种类型的数组。
以下代码为一个常规对象增加了一些属性使其变成类数组对象,然后遍历生成的伪数组的“元素”:
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];
例如Arguments对象就是一个类数组对象。在客户端JavaScript中,一些DOM方法(如
document.getElementsByTagName())也返回类数组对象。下面有一个函数可以用来检测类数
组对象:
//判定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.length<2^32
return true;//o是类数组对象
else
return false;//否则它不是
}
JavaScript数组方法是特意定义为通用的,因此它们不仅应用在真正的数组而且在类数组对象上
都能正确工作。在ECMAScript 5中,所有的数组方法都是通用的。在ECMAScript 3中,除了
toString()和toLocaleString()以外的所有方法也是通用的。(concat()方法是一个特例:
虽然可以用在类数组对象上,但它没有将那个对象扩充进返回的数组中。)既然类数组对象没有
继承自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,0)//=>["a","b","c"]:真正数组的副本
Array.prototype.map.call(a,function(x){
return x.toUpperCase();
})//=>["A","B","C"]:
ECMAScript 5数组方法是在Firefox 1.5中引入的。由于它们的写法的一般性,Firefox还将这
些方法的版本在Array构造函数上直接定义为函数。使用这些方法定义的版本,上述例子就可以
这样重写:
var a={"0":"a","1":"b","2":"c",length:3};//类数组对象
Array.join(a,"+")
Array.slice(a,0)
Array.map(a,function(x){return x.toUpperCase();})
当用在类数组对象上时,数组方法的静态函数版本非常有用。但既然它们不是标准的,不能期望
它们在所有的浏览器中都有定义。可以这样书写代码来保证使用它们之前是存在的:
Array.join=Array.join||function(a,sep){
return Array.prototype.join.call(a,sep);
};
Array.slice=Array.slice||function(a,from,to){
return Array.prototype.slice.call(a,from,to);
};
Array.map=Array.map||function(a,f,thisArg){
return Array.prototype.map.call(a,f,thisArg);
}
3.作为数组的字符串
在ECMAScript 中,字符串的行为类似于只读的数组。除了用charAt()方法来访问单个的字符以
外,还可以使用方括号:
var s=test;
s.charAt(0)//=>"t"
s[1]//=>"e"
当然,针对字符串的typeof操作符仍然返回"string",但是如果给Array.isArray()传递字符串,
它将返回false。
可索引的字符串的最大的好处就是简单,用方括号代替了charAt()调用,这样更加简洁、可读并且
可能更高效。不仅如此,字符串的行为类似于数组的事实使得通用的数组方法可以应用到字符串上。
例如:
s="JavaScript"
Array.prototype.join.call(s,"")//=>"JavaScript"
Array.prototype.filter.call(s,//过滤字符串中的字符
function(x){
return x.match(/[^aeiou]/);//只匹配非元音字母
}).join("")//=>"JvScrpt"
请记住,字符串是不可变值,故当把它们作为数组看待时,它们是只读的。如push()、sort()、
reverse()和splice()等数组方法会修改数组,它们在字符串上是无效的。不仅如此,使用数组方
法来修改字符串会导致错误:出错的时候没有提示。
网友评论