JavaScript数组如何在物理内存中表示?

时间:2013-12-02 04:47:14

标签: javascript arrays dynamic weakly-typed

据我了解,我可以将混合数据存储在JavaScript数组中,以及将数组中的任何元素更改为其他类型。解释器如何跟踪任何元素在物理内存中的位置。如果我将元素更改为更大的数据类型,如何防止覆盖下一个元素中的数据。

我假设数组只存储对实际对象的引用,并且当放置在数组中时,基元会包含在幕后。

假设是这种情况,如果我对原始变量有不同的句柄并更改存储在数组中的值是同步保持吗?

我知道我可能已经回答了我自己的问题,但我不确定,我找不到有关此事的任何信息。

3 个答案:

答案 0 :(得分:40)

通常,数组会分配一个固定长度的连续内存块。但是,在Javascript中,数组是具有特殊构造函数和访问器方法的Object类型。

这意味着,声明如下:

var arr = new Array(100000);

不分配任何内存!实际上,它只是设置数组中length属性的值。构造数组时,无需在自动增长时声明大小。所以,你应该改用它:

var arr = [];

Javascript中的数组是稀疏的,这意味着并非数组中的所有元素都可能包含数据。换句话说,只有实际包含数据的元素存在于数组中。这减少了阵列使用的内存量。值由键定位,而不是由偏移定位。它们只是一种方便的方法,并不打算用于复杂的数值分析。

Javascript中的数组没有输入,因此元素的值可以是对象,字符串,数字,布尔值,函数或数组。数组和对象之间的主要区别是length属性,其值大于数组中的最大整数键。

例如:

你可以创建一个空数组并在索引0和索引99处添加两个元素。长度为100,但数组中的元素数量为2。

var arr = [];
arr[0] = 0;
arr[99] = {name: "John"};
console.log(arr.length); // prints 100
arr; // prints something like [0, undefined × 98, Object { name: "John"}]

直接回答您的问题:

  

Q值。我的理解是,我可以将混合数据存储在JavaScript数组中,以及将数组中的任何元素更改为其他类型。解释器如何跟踪任何元素在物理内存中的位置?另外,如果我将元素更改为更大的数据类型,如何防止覆盖下一个元素中的数据?

     

一个。如果您已阅读上述评论,您现在可能已经知道了。在Javascript中,数组是Hashtable对象类型,因此解释器不需要跟踪物理内存,并且更改元素的值不会影响其他元素,因为它们不存储在连续的内存块中。 / p>

-

  

Q值。我假设数组只存储对实际对象的引用,并且当放置在数组中时,基元被包裹在场景后面。假设是这种情况,如果我对原始变量有不同的句柄并更改存储在数组中的值是同步保持吗?

     

一个。不,原始人没有被包裹。更改分配给数组的原语不会更改数组中的值,因为它们是按值存储的。另一方面,对象通过引用存储,因此更改对象值将反映该数组中的更改。

以下是您可以尝试的示例:

var arr = [];
var obj = { name: "John" };
var isBool = true;

arr.push(obj);
arr[1] = isBool;

console.log(arr[0]); // print obj.name
console.log(arr[1]); // print true

obj.age = 40;        // add age to obj
isBool = false;      // change value for isBool

console.log(arr[0]); // value here will contain age
console.log(arr[1]); // value here will still be true

另外,请注意,当您使用以下两种方式初始化数组时,它会有不同的行为:

var arr = new Array(100);
console.log(arr.length);        // prints 100
console.log(arr);               // prints []

var arr2 = new Array(100, 200);
console.log(arr2.length);       // prints 2
console.log(arr2);              // prints [100, 200]

如果你想使用Javascript Arrays作为连续的内存块,你应该考虑使用TypedArray。 TypedArray允许您将一块内存分配为字节数组,并更有效地访问原始二进制数据。

您可以阅读ECMA-262 spec (ver 5.1)

,详细了解Javascript的复杂性

答案 1 :(得分:7)

这是一些值得思考的问题。我做了一些JavaScript引擎实现的a jsperf to test a simple array optimization

测试用例创建两个数组,每个数组包含一百万个元素。 a数组仅包含数字;除了作为对象的第一个元素外,b数组包含相同的数字:

var a = [ 0 ], b = [ { valueOf: function() { return 0; } } ];
for( var i = 1;  i < 1000000;  ++i ) {
    a[i] = b[i] = i;
}

valueOf数组的第一个元素中对象的b属性返回0,因此算术将与第一个数组相同。

然后这两个测试只是将两个数组的所有值相加。

快速阵列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += a[i];
}

慢阵列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += b[i];
}

从jsperf的测试结果可以看出,在Chrome中,数字数组的速度提高了约5倍,在Firefox中,数字数组的速度提高了约10倍,在IE浏览器中的速度提高了约2倍。

这并没有直接显示用于数组的内部结构,但它给出了一个很好的指示,即两者之间存在很大差异。

答案 2 :(得分:0)

公认的答案是

  

[数组构造]不分配任何内存!

那不一定是真的。将数组存储为对象中的无序键值对,效率很低,因为每次查找都是搜索。即使对条目进行了排序,它仍然会分配比所需更多的内存。因此,许多引擎以几种不同的方式表示阵列,以保护内存并优化性能。在Michael Geary的示例中也可以看出,仅包含数字的数组已得到优化(details for V8)。如果基础表示形式具有固定大小且无法轻松缩放,则引擎有时可能会决定为新创建的数组分配一些空插槽。