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

use*_*052 46 javascript arrays dynamic weakly-typed

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

我假设数组只存储对实际对象的引用,并且当放置在数组中时,基元被包裹在场景后面.

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

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

Nic*_*ick 49

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

这意味着,声明如下:

var arr = new Array(100000);
Run Code Online (Sandbox Code Playgroud)

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

var arr = [];
Run Code Online (Sandbox Code Playgroud)

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"}]
Run Code Online (Sandbox Code Playgroud)

直接回答您的问题:

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

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

-

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

另请注意,在以下两种方式初始化数组时,它具有不同的行为:

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]
Run Code Online (Sandbox Code Playgroud)

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

您可以通过阅读ECMA-262规范(版本5.1)了解有关Javascript错综复杂的更多信息.

  • 很好的答案,但我认为值得注意的是,这里有很多关于实施定义的事情.例如,在SpiderMonkey(早期Mozilla JS实现的主干)中,数组被实现为jsvals的密集C数组,直到您对它们执行某些操作,此时引擎可以将它们切换到您下面的对象. (11认同)

Mic*_*ary 8

这是一些值得思考的东西.我制作了一个jsperf来测试一些JavaScript引擎实现的简单数组优化.

测试用例创建两个数组,每个数组包含一百万个元素.该a数组仅包含数字; 该b数组包含除了它是一个对象的第一元件相同的数字:

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

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

然后,这两个测试简单地将两个数组的所有值相加.

快速阵列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += a[i];
}
Run Code Online (Sandbox Code Playgroud)

慢阵列:

var x = 0;
for( var i = 0;  i < 1000000;  ++i ) {
    x += b[i];
}
Run Code Online (Sandbox Code Playgroud)

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

这并没有直接揭示用于阵列的内部结构,但它给出了一个非常好的指示,即两者彼此完全不同.

  • @yangguang1029 不,事实并非如此。它在第一次加法时将对象强制转换为数字。 (2认同)

Jon*_*lms 6

接受的答案说

【数组构造】不分配任何内存!

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