在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