在ES5中,數組的length屬性是這么規定的:
15.4.5.2 length
數組對象的
length屬性是一個數據屬性,該屬性的值始終從數值上大於所屬數組的任何一個索引號
.
length屬性的初始特性為
{ [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }.
數組的屬性可以分為三種:length屬性,索引屬性,其他屬性.和普通對象相比,數組對象特殊的地方就是它的length屬性和索引屬性,為了對數組的屬性做特殊處理,ES5標准專門規定了一個數組專用的[[DefineOwnProperty]]內部方法(15.4.5.1),以區別於其他對象所用的[[DefineOwnProperty]]內部方法(8.12.9).
1. 修改數組的length屬性會不會影響數組的索引屬性?
數組的索引屬性(通常稱之為索引或下標)是那些屬性名為數字形式的屬性(實際上是字符串).而且數字范圍必須處於0到232-2之間.
本節要講的這些表現主要規定在ES5 15.4.5.1-3中.
1.1. 以賦值方式修改length屬性:
以賦值方式只能修改length屬性的值(也就是value特性),本節給出的例子在目前所有主流引擎上表現都相同.
1.1.1. 手動增大數組的length屬性不會影響數組的索引屬性:
> var a = [0,1,2,3,4] > a [0, 1, 2, 3, 4] > a.length = 10 10 > a [0, 1, 2, 3, 4, , , , , ,] //手動增大length屬性並不會自動補全所缺少的索引屬性,只是有些交互環境在顯示數組的時候會把缺少的索引屬性以空字符串+逗號的方式顯示出來. > print(a) 0,1,2,3,4,,,,, //Array.prototype.toString方法也會把那些缺少的索引屬性顯示出來 > Object.keys(a) ["0", "1", "2", "3", "4"] //但實際上索引屬性並沒有增加,最大的索引屬性還是4 > a.length = Math.pow(2,32) RangeError: invalid array length //為length屬性賦過大的值會拋出異常
1.1.2. 手動減小數組的length屬性會影響數組的索引屬性:
> var a = [0,1,2,3,4]
> a
[0, 1, 2, 3, 4]
> a.length = 3 3 > a [0, 1, 2] //手動減小length屬性會截掉多出來的索引屬性 > Object.defineProperty(a,"1",{configurable:false}) //如果需要截掉的索引屬性中有一個是不可刪除的,也就是不可配置的,那么... [0, 1, 2] > a.length = 1 1 >a [0, 1] //截取操作會被這種不可刪除的索引屬性阻止 >a.length 2 //length屬性也會被自動設置成為最大的索引號+1
1.2. 以定義方式修改length屬性:
如果你還不知道對象屬性的定義操作和賦值操作有什么區別,推薦看看我的另一篇文章[譯]JavaScript中的屬性:定義和賦值的區別.
以定義方式不僅能修改length屬性的value特性,還能修改其他的特性比如writable特性.以定義方式修改數組的length屬性在目前所有主流引擎上的表現有很多不同.
Chrome 25(v8):
>var a = [0,1,2,3,4] >Object.defineProperty(a,"length",{value:10}) //以定義方式修改length屬性的值 [0, 1, 2, 3, 4, undefined × 5] >a.length //修改成功 10 >Object.defineProperty(a,"length",{writable:false}) //修改length屬性為只讀 [0, 1, 2, 3, 4, undefined × 5] >Object.getOwnPropertyDescriptor(a,"length") Object {value: 10, writable: false, enumerable: false, configurable: false} //length屬性現在是不可配置加不可寫的 >a.length = 100 //以賦值方式修改length屬性的值 100 >a.length //的確沒有更改成功 10 >(function(){"use strict";a.length=100})() //嚴格模式下會報錯 TypeError: Cannot assign to read only property 'length' of [object Array] >Object.defineProperty(a,"length",{value:100}) //再以定義方式修改length屬性的值 TypeError: Cannot redefine property: length //也不能修改 >a.push(10) //通過push方法修改數組 11 >a [0, 1, 2, 3, 4, undefined × 5, 10] //push進去了 >a.length 11 //length屬性也被修改了 >a[100] = 100 //添加新的索引元素 100 >a.length 101 //length屬性也被修改了
Opera12.12(Carakan) :
opera中的表現和chrome基本相同.
IE9(Chakra):
IE9中的表現和chrome的區別是,length屬性如果已經被重定義成只讀的,則通過任何方式都不能修改它的值.
>> Object.defineProperty(a,"length",{value:100}) "無法修改不可寫的屬性“length”" >> a.push(10) //拋出異常 "對象不支持此操作" >> a[100] = 10 //因為不能修改length屬性的值,所以這樣的屬性添加操作也會靜默失敗 10 >> a[100] + "" "undefined"
>> a.length
10
Firefox 20(SpiderMonkey):
> var a = [0,1,2,3,4] > Object.defineProperty(a,"length",{value:10}) InternalError: defining the length property on an array is not currently supported
//Firefox完全不支持重新定義數組的length屬性,但未來會修復.詳見https://bugzilla.mozilla.org/show_bug.cgi?id=591059
Safari 5.17(JavaScriptCore):
>var a = [0,1,2,3,4] >Object.defineProperty(a,"length",{value:10}) //靜默失敗 [0, 1, 2, 3, 4] >a.length 5 //length屬性沒有變 >Object.defineProperty(a,"length",{writable:false}) //靜默失敗 [0, 1, 2, 3, 4] >JSON.stringify(Object.getOwnPropertyDescriptor(a,"length")) "{"value":5,"writable":true,"enumerable":false,"configurable":false}" //屬性描述符沒有變
//Safari也完全不支持重新定義數組的length屬性,但不拋出異常
哪個瀏覽器的表現是最合理的,這我不知道.但我們應該避免數組length屬性的重定義操作,不過一般也不需要這樣做.
2. 修改數組的索引屬性會不會影響數組的length屬性?
本節要講的這些表現主要規定在ES5 15.4.5.1-4中.
2.1. 增大數組的最大索引號會影響數組的length屬性 :
> var a = [0,1,2,3,4]
> a
[0, 1, 2, 3, 4]
> a[10] = 10
10
> a
[0, 1, 2, 3, 4, , , , , , 10] //這種數組通常稱之為"稀疏數組",對應的是"密集數組"
> a.length
11 //length屬性的值被自動更改為最大索引號10再加上1
> a[Math.pow(2,32)-1] = "x" //超過2^32-2的話(4294967295)
"x"
> a
[0, 1, 2, 3, 4, , , , , , 10] //4294967295不在索引里面
> a.length
11 //length屬性的值沒有變
>a[Math.pow(2,32)-1] //4294967295只能算個普通屬性
"x"
2.2. 刪除索引號最大的索引屬性不會影響數組的length屬性:
> var a = [0, 1, 2, 3, 4, , , , , , 10]
> a.length
11
> delete a[10]
true > a
[0, 1, 2, 3, 4, , , , , , ,] //數組現在只有5個索引屬性 > a.length
11 //但length屬性沒有減小,還是11