[譯]JavaScript中的數組


原文: http://www.2ality.com/2012/12/arrays.html

本文要解釋一下Javascript中的數組是如何工作的,你將會知道,它們比你想的更像普通對象. 

1.概述

在Javascript中,對象是一個從字符串到值的映射.數組也是對象,只是包含有一些特殊的屬性:

  1. 數組索引(下標):如果一個數組對象的屬性的數字值(實際上是字符串值)是一個小於232-1的非負整數,則該屬性就會被看成是一個數組索引.

  2. "length"屬性:該屬性的值是一個非負整數,表示了數組的長度.這個長度的值通常是數組的最大索引轉換成數字后,再加1.

下面要說的這個表現有時候會讓人感到震驚,尤其對於那些剛剛從其他語言轉來的人,就是:JavaScript中的數組索引實際上是字符串.(在引擎內部,為了獲得更快的訪問速度,數組索引通常是用數字來實現的.但是規范就是這么規定的,程序員們看到的表現也是這樣的).例如:

> var arr = ['a', 'b', 'c'];
> arr['0']
'a'
> arr[0]
'a'
因為 0不是一個合法的標識符(identifier),所以點符號( arr.0)會產生一個語法錯誤.因此,你必須使用方括號.方括號運算符會將它的操作數轉換成字符串,這就是上面的 arr[0]為什么能正常工作的原因.數組索引被限制在32比特的范圍內.類似於方括號運算符, in運算符也會將它的第一個操作數轉換成字符串.這就是為什么能用數字來檢查給定索引的數組元素是否存在:
> 2 in [ 'a', 'b', 'c' ]
true
> 3 in [ 'a', 'b', 'c' ]
false
文章下面的幾小節將更加深入的講解數組是如何工作的.

2.稀疏數組

正如我們看到的,數組也是從字符串到值的映射.這就意味着數組可以有孔(hole), 一個有孔的數組稱之為稀疏數組(sparse array).稀疏數組中索引的個數小於length屬性的值.在使用數組字面量定義數組時,你可以通過在逗號前面不寫任何值來創建一個孔(最尾部的逗號會被忽略).數組的遍歷方法比如 forEach和 map會忽略掉數組中的孔 .下面,讓我們比較一下稀疏數組和密集數組(dense array):
> var sparse = [ , , 'c' ];
> var dense  = [ undefined, undefined, 'c' ];

> 0 in sparse
false
> 0 in dense
true

> for(var i=0; i<sparse.length; i++) console.log(sparse[i]);
undefined
undefined
c
> for(var i=0; i<dense.length; i++) console.log(dense[i]);
undefined
undefined
c

> sparse.forEach(function (x) { console.log(x) });
c
> dense.forEach(function (x) { console.log(x) });
undefined
undefined
c

> sparse.map(function (x,i) { return i });
[ , , 2 ]
> dense.map(function (x,i) { return i });
[ 0, 1, 2 ]

> sparse.filter(function () { return true })
[ 'c' ]
> dense.filter(function () { return true })
[ undefined, undefined, 'c' ]

3.數組索引

關於什么樣的屬性才能稱之為數組索引,ECMAScript規范有這樣的定義.一個字符串s必須滿足下面兩個要求,才能成為數組的索引:
 
   要求1: 字符串s 解析成為一個無符號32位整數之后,再轉換成字符串的值必須要和s相同.
   要求2: 字符串s 解析成為整數之后的值必須小於232−1 (數組的最大長度).
 
因此,如果按照數值大小比較,一個數組的索引s必須滿足下面的范圍表達式:
0 ≤ s < 2 32−1
ToUint32是一個規范內部的方法,它可以將其他的值轉換成無符號32位整數.你也可以使用JavaScript代碼來實現這個內部方法 [1]:
function ToUint32(x) {
    return x >>> 0;
}
上面的要求1表明了:雖然很多字符串都可以被轉換成一個無符號32位整數,但只有其中的少數可以作為合法的數組索引.比如:
> ToUint32('0')
0
> ToUint32('00')
0
> ToUint32('03')
3
> ToUint32('abc')
0
> ToUint32(Math.pow(2,32)+3)
3
上例中只有第一條語句中參數"0"滿足了要求1,是個有效的數組索引.

所有不滿足數組索引要求的字符串都會被看成普通屬性:

> var arr = ['a', 'b', 'c'];
> arr['0']
'a'
> arr['00']
undefined

4.length

譯者注:很巧,上周我剛剛寫過一篇文章: JavaScript:數組的length屬性
數組length屬性的值的范圍是:
0 ≤ l ≤ 2 32−1 (32位)

4.1 索引屬性的影響

在有新的元素添加時,數組的length屬性會自動增大:
> var arr = [];
> arr.length
0
> arr[0] = 'a';
> arr.length
1

4.2 減小length屬性

如果length屬性當前的值為 l,被賦一個新的值 l',且 l' 比原值l小,那么在下面范圍內的索引都會被刪除.
l'i < l
例如:
> var arr = [ 'a', 'b', 'c' ];
> arr.length
3
> 2 in arr
true
> arr.length = 2;
2
> 2 in arr
false

4.3 增大length屬性

增大length屬性的值會創建一個稀疏數組:
> var arr = ['a'];
> arr.length = 3;
> arr
[ 'a', , ,]

4.4 length屬性的有效值

你可以給length屬性賦任何值,引擎內部會使用 ToUint32方法將所賦的值轉換成數字,轉換成的數字必須滿足length屬性值的合法范圍.例如:
> ToUint32('0')  //*
0
> ToUint32('000')  //*
0
> ToUint32('-1')
4294967295
> ToUint32(Math.pow(2,32)-1)  //*
4294967295
> ToUint32(Math.pow(2,32))
0
> ToUint32('abc')
0
上面所有帶星號的length賦值是有效的,其他的都是無效的:
> Number('0')
0
> Number('000')
0
> Number('-1')
-1
> Number(Math.pow(2,32)-1)
4294967295
> Number(Math.pow(2,32))
4294967296
> Number('abc')
NaN
你可以測試一下:
> [].length = -1
RangeError: Invalid array length
> [].length = Math.pow(2,32)
RangeError: Invalid array length
> [].length = 'abc'
RangeError: Invalid array length

5.數組實例

數組的對象實例和普通對象非常類似,只是在定義下面兩種屬性時會有一些額外的操作:
  • 數組索引:可能會增大length屬性的值.
  • "length"屬性:過大的話會拋出異常,如果新值小於舊值的話會刪除超出的元素.
所有其他屬性的處理都和普通對象完全相同.

6.超出限制

如果你使用了一個不在索引范圍內的索引的話,會發生什么?答案就是該索引會被看成一個普通屬性.比如我們設置一個過大的索引值.
> var arr = ['a', 'b'];
> arr[Math.pow(2,32)-1] = 'c';
> arr
[ 'a', 'b' ]
> arr.length
2
> arr[Math.pow(2,32)-1]
'c'
如果你設置一個超大的length屬性,也會拋出異常:
> var arr = new Array(Math.pow(2,32)-1)  // max length
> arr.push('x')
RangeError: Invalid array length

譯者注:這個地方有個讓人吃驚的表現,我剛好剛講過:JavaScript:數組能越界?

7.建議

使用數組時的兩個建議:
  • 假裝數組索引就是數字.這正是引擎內部的實現方式,而且這也是ECMAScript未來的大方向.
  • 在對待數組時不要太過聰明.只需要遵循標准的處理模式,引擎通常會知道你想干什么,從而進行對應的優化.不需要你特殊處理.文章“Performance Tips for JavaScript in V8” (作者是Chris Wilson)就講了幾個數組操作相關的建議.

8.相關文章

  1. Integers and shift operators in JavaScript
  2. [譯]JavaScript中的稀疏數組與密集數組


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM