翻译:道奇
做者:Dmitri Pavlutin
原文:The Magic Behind Array Length Property数组
开发人员天天都会处理数组,做为一个集合,它有一个很重要的用于查询的属性是项(item)的数量:Array.prototype.length
。安全
在JavaScript中,length
并不老是指的现有元素(对于稀疏矩阵)的数量,修改属性也可能会移除元素。bash
下面让咱们揭开这个属性背后魔法的面纱。app
"数组
length
是一个无符号的32
位整数,其数值大于数组中索引的最大值"函数
这个属性根据所指定的数组类型不一样行为也不一样,下面列举他们:当数组的元素的索引是连续的且从0
开始,数组就是密集(dense
)的,例如[1, 3, 4]
是密集的,由于索引是连续的:0
, 1
和2
。当数组的元素不是从0
开始的连续索引,那么它就是稀疏(sparse
)的,例如[1, ,4, 6]
是稀疏的,由于元素的索引是不连续的:0
, 2
和3
。ui
length
经常使用的就是肯定元素的数量,这在密集的集合类型下是正确的:spa
var fruits = ['orange', 'apple', 'banana']; //fruits密集数组
fruits.length // 打印3, 实际的元素数量
fruits.push('mango');
fruits.length // 打印4, 添加了一个元素
var empty = [];
empty.length // 打印 0, 空数组
复制代码
能够在JS Bin中看这个例子prototype
密集数组没有空槽的数组元素,元素的数量至关于最大索引值 + 1
,在[3, 5, 7, 8]
中最大的索引是元素8
的索引3
,所以数组的大小是3 + 1 = 4
。翻译
在稀疏数组中,length
比最大索引大,可是它并不表明真实的元素数量。当查询length
,它比元素的总数大。这是由于数组中存在空槽位置的缘由。code
var animals = ['cat', 'dog', , 'monkey']; // animals是稀疏的
animals.length // 打印 4,可是实际数量是3
var words = ['hello'];
words[6] = 'welcome'; //最大的索引是6,words是稀疏数组
words.length //打印 7, 基于最大索引
复制代码
当添加或移除元素时,length
仅会根据最大索引进行改变,任何不会影响数组最大索引的修改都不会影响length
,例如,使用delete
。
var colors = ['blue', 'red', 'yellow', 'white', 'black'];
colors.length // 打印5
delete colors[0]; // 移除第一个元素 'blue'
// 数组变成稀疏数组
colors.length // 依然打印5,由于最高的索引是4 ,没有发生改变
复制代码
能够在JS Bin中看这个例子
在上述的分析中,length
是只读的。可是JavaScript容许修改这个属性,length
的修改会怎样影响数组取决于新的值和现有的最大的索引,这个操做也能够移除元素或使数组变成稀疏数组。当新的length
值小于或等于最大索引时,任何元素,若是它的索引大于或等于新组大小就会被移除,这种作法的一个有用的场景就是移除数组的最后一个元素。
var numbers = [1, 3, 5, 7, 8];
numbers.length = 3; // 修改数组length
numbers // 打印 [1, 3, 5], 元素7和8被移除
复制代码
若是使用大于最大索引(或使用大于当前length
的数值)的数值,数组就会变成稀疏的,这个用处不大。
var osTypes = ['OS X', 'Linux', 'Windows'];
osTypes.length = 5; // 建立一个稀疏数组,索引索引3和4的元素不存在
osTypes // 打印 ['OS X', 'Linux', 'Windows', undefined,undefined ]
复制代码
能够在JS Bin中看这个例子
给length
指定数字以外的类型也是能够的,JavaScript会将这个原始值转换为数字,若是转换的结果是NAN
或是小于0
的数字,就会抛出没有捕捉的范围异常:元素数组长度
var numbers = [1, 4, 6, 7];
numbers.length = '2'; // '2'被转换为数字 2
numbers.length = 'not-number'; // 抛出没有捕捉的范围异常:元素数组长度
numbers.length = -2; // 抛出没有捕捉的范围异常:元素数组长度
复制代码
修改数组length
,经过delete
移除元素,使用[新索引]
添加元素都是经过建立稀疏数组而产生潜在问题的根源。结果是length
值不一致,JavaScript提供了更安全的替代方案。
使用Array.prototype.push()
向数组末尾添加元素,经过pop()
移除最后一个元素 ,使用unshift()
向数组起始端插入元素和使用shift()
移除第一个元素。对于更复杂的插入、删除或替换,splice()
是足够强大的。
var companies = ['Apple', 'Dell'];
companies.push('ASUS'); // 向末尾添加元素
companies // 打印 ['Apple', 'Dell', 'ASUS']
companies.pop(); // 打印 "ASUS". 移除最后一个元素
companies // 打印 ['Apple', 'Dell']
companies.shift(); // 打印 "Apple". 移除第一个元素
companies // 打印 ["Dell"]
companies.splice(1, 0, "Microsoft", "HP"); // 添加两空公司
companies // 打印 ["Dell", "Microsoft", "HP"]
companies.length // 打印 3. 数组是密集的
复制代码
能够在JS Bin中看这个例子
在极少数状况下,数组多是稀疏的。靠length
决定元素的数量是不安全的,能够使用一个帮助函数来处理缺乏的元素:
/**
* 计算一个稀疏数组的元素数量
* @param {Array} collection
* @return {number}
*/
function count(collection) {
var totalCount = 0;
for (var index = 0; index < collection.length; index++) {
if (index in collection) {
totalCount++;
}
}
return totalCount;
}
复制代码
in
运算符能够用于判断这个对象是否有属性,它在检查元素在指定的索引下是否存在是很是有用的。
就像在这篇文章中看到的,length
是一个具备复杂行为的属性。 大部分状况下,都能正常工做,可是当处理稀疏数组并修改length
时最好当心一点。 另外一种方法是彻底避免修改此属性并使用splice()
方法。