1. 程式人生 > >js基礎--陣列(3)

js基礎--陣列(3)

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()等陣列方法會修改陣列,它們在字串上是無效的。不僅如此,使用陣列方
法來修改字串會導致錯誤:出錯的時候沒有提示。