据我所知,JavaScript数组可以存储混合数据类型,并且可以将任何元素更改为其他类型。解释器如何跟踪每个元素在物理内存中的位置?如果我将一个元素更改为较大的数据类型,如何防止覆盖下一个元素的数据。
我假设数组只存储实际对象的引用,并且当放置在数组中时,基本类型会在后台进行包装。
假设情况是这样的,如果我有一个与基本变量不同的句柄,并更改存储在数组中的值,同步性是否得到维护?
我知道我可能已经回答了自己的问题,但我不确定,也找不到任何相关的信息。
据我所知,JavaScript数组可以存储混合数据类型,并且可以将任何元素更改为其他类型。解释器如何跟踪每个元素在物理内存中的位置?如果我将一个元素更改为较大的数据类型,如何防止覆盖下一个元素的数据。
我假设数组只存储实际对象的引用,并且当放置在数组中时,基本类型会在后台进行包装。
假设情况是这样的,如果我有一个与基本变量不同的句柄,并更改存储在数组中的值,同步性是否得到维护?
我知道我可能已经回答了自己的问题,但我不确定,也找不到任何相关的信息。
通常,数组会分配一个固定长度的连续内存块。然而,在Javascript中,数组是带有特殊构造函数和访问器方法的对象类型。
这意味着,像下面这样的语句:
var arr = new Array(100000);
实际上不会分配任何内存!事实上,它只是设置了数组中长度属性的值。当你构造一个数组时,你不需要声明一个大小,因为它们会自动增长。所以,你应该使用这个:
var arr = [];
Javascript中的数组是稀疏的,这意味着数组中并不是所有元素都包含数据。换句话说,只有实际包含数据的元素存在于数组中,这减少了数组使用的内存量。这些值由键而非偏移量定位。它们只是一种方便的方法,不适用于复杂的数值分析。
Javascript中的数组没有类型限制,因此一个元素的值可以是对象、字符串、数字、布尔值、函数或数组。数组和对象之间的主要区别在于长度属性,该属性的值大于数组中最大的整数键。
例如:
您可以创建一个空数组,并在索引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数组可以存储混合数据,也可以将数组中的任何元素更改为其他类型。解释器如何跟踪每个元素在物理内存中的位置?如果我将一个元素更改为较大的数据类型,如何防止覆盖下一个元素中的数据?
A:如果您已经阅读了我上面的评论,那么您可能已经知道了。在JavaScript中,数组是Hashtable对象类型,因此解释器不需要跟踪物理内存,更改元素的值也不会影响其他元素,因为它们不是存储在连续的内存块中。
--
Q:我假设数组仅存储对实际对象的引用,并且当将原始值放入数组时,在后台进行了包装。如果这是情况,如果我有一个原始变量的不同句柄,并更改存储在数组中的值,是否保持同步性?
A:不,原始值没有被包装。将分配给数组的原始值更改不会更改数组中的值,因为它们是按值存储的。另一方面,对象是按引用存储的,所以更改对象的值将反映在该数组中。
这里有一个您可以尝试的示例:
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数组用作连续的内存块,可以考虑使用TypedArray。TypedArray允许您将一块内存分配为字节数组,并更高效地访问原始二进制数据。以下是一些值得思考的内容。我制作了一个jsperf测试简单数组优化,其中一些JavaScript引擎实现了这种优化。
测试案例创建两个包含一百万个元素的数组。 a
数组仅包含数字;而 b
数组包含相同的数字,除了第一个元素是一个对象:
var a = [ 0 ], b = [ { valueOf: function() { return 0; } } ];
for( var i = 1; i < 1000000; ++i ) {
a[i] = b[i] = i;
}
在数组b的第一个元素中的对象的valueOf
属性返回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倍。
这并没有直接揭示数组使用的内部结构,但它非常清楚地表明两者之间存在相当大的差异。
被接受的答案说
[数组构造]不会分配任何内存!
这并不一定是真的。将数组存储为无序键值对(如对象)非常低效,因为每次查找都需要搜索。即使条目已排序,它仍会分配比所需更多的内存。因此,许多引擎以几种不同的方式表示数组,以节省内存并优化性能。这也可以在Michael Geary的示例中看到,仅包含数字的数组得到了优化( V8的详细信息)。有时,引擎可能会决定为新创建的数组分配一些空插槽,如果底层表示具有固定大小且难以轻松缩放(例如,数组列表)。
x=3
,在某个地方分配给一个数组,然后突然看到x==4
)。 - user2357112