1.数组内部
数组是一种特殊的对象。使用方括号来访问属性 arr[0] 实际上是来自于对象的语法。它其实于 obj[key] 相同,其中 arr 是对象,而数字用作键(key)。
它们扩展了对象,提供了特殊的方法来处理有序的数据集合以及 length 属性。但从本质上讲,它仍然是一个对象。
记住,在 JavaScript 中只有 8 种基本的数据类型。数组是一个对象,因此其行为也像一个对象。
例如,它是通过引用来复制的:
let fruits = ["Banana"];
let arr = fruits; //通过引用复制(两个变量引用同一个数组)
alert(arr === fruits); //true
arr.push("Pear"); //通过引用修改数组
alert(fruits); //Banana, Pera
......但是数组真正特殊的是它们的内部实现。JavaScript 引擎尝试把这些元素一个接一个地存储在连续的内存区域,而且还有一些其它的优化,以使数组运行得非常快。
但是,如果我们不像”有序集合“那样使用数组,而是像常规对象那样使用数组,这些优化将不生效。
例如,从技术上讲,我们可以这样做:
let fruits = []; //创建一个数组
fruits[99999] = 5; //分配索引远大于数组长度的属性
fruits.age = 25; //创建一个具有任意名称的属性
这是可以的,因为数组是基于对象的。我们可以给它们添加任何属性。
但是 JavaScript 引擎会发现,我们在像使用常规对象一样使用数组,那么针对数组的优化就不再适用了,然后对应的优化就会被关闭,这些优化所带来的优势也就荡然无存了。
数组误用的几种方式:
1.添加一个非数字的属性,比如 arr.test = 5.
2.制造空洞,比如:添加 arr[0],然后添加 arr[1000](它们中间什么都没有)。
3.以倒序填充数组,比如 arr[1000], arr[999]等等。
请将数组是为作用于 有序数据 的特殊结构。它们为此提供了特殊的方法。数组在 JavaScript 引擎内部是经过特殊调整的,是的更好地作用于连续的有序数据,所以请以正确的方式使用数组。如果你需要任意键值,那很有可能实际上你需要的是常规对象{}。
2.数组性能
shift/unshift
push/pop 方法运行的比较快,而 shift/unshift 比较慢。
为什么作用于数组的末端会比首端快呢?让我们看看在执行期间都发生了什么:
fruits.shift(); //从首端取出一个元素
只获取并移除键值为 0 对应的元素是不够的。其它元素也需要被重新编号。
shift 操作必须做三件事:
1.移除索引为 0 的元素。
2.把所有的元素向左移动,把索引 1 改成 0,2 改成 1 以此类推,对其重新编号。
3.更新 length 属性。
数组里的元素越多,移动它们就要花越多的时间,也就意味着越多的内存操作。
unshift 也是一样:为了在数组的首端添加元素,我们首先需要将现有的元素向右移动,增加它们的索引值。
push/pop
那 push/pop 是什么样的呢?它们不需要移动任何东西。如果从末端移除一个元素,pop 方法只需要清理索引值并缩短 length 就可以了。
pop 操作的行为:
fruits.pop(); //从末端取走一个元素
pop 方法不需要移动任何东西,因为其它元素都保留了各自的索引。这就是为什么 pop 会特别快。
push 方法也是一样的。