我有一些遗留的JS代码,用[]创建一个巨大的嵌套对象结构。 代码有点像这样
var data = [];
data ["first"] = [];
data ["first"]["second"] = [];
data ["first"]["second2"] = "hello";
大约250多KB的javascript,相当大。当我尝试用requirejs将其包装到另一个requirejs模块中时,它会抛出Out Of Memory错误。
如果我使用{},那么错误就会消失。
我周末在[]和{}上做了一些功课,原因似乎是使用关联数组作为嵌套词典可能在Javascript中泄漏,因为数组扩展了一个JS对象,可能会有更多的更新内容。将新对象附加到其中。但它是否解释了内存消耗问题?或者它与Requirejs如何解析模块的对象有关?
我没有足够的知识来做JS内存检测,并在浏览器引擎中使用{}或[]进行比较,因此很难得出结论。关于如何衡量{}与[]的任何暗示或建议都将受到欢迎。
更新:我昨天通过节点尝试了一些sizeOf()。我使用了所有现有的:" js-sizeof"," object-sizeof"," sizeof"
代码:
var sizeof = require('object-sizeof');
var obj = [];
obj['ball'] = 'hello';
obj['air'] = 'hello';
obj['ball']['fire'] = 'world';
obj['ball']['ice'] = [];
console.log(sizeof(obj));
var obj2 = {};
obj2['ball'] = 'hello';
obj2['air'] = 'hello';
obj2['ball']['fire'] = 'world';
obj2['ball']['ice'] = [];
console.log(sizeof(obj2));
结果是
[]:34 {}:34
sizeOf实际上是相同的。但是,[]可能会发生其他可能触发内存不足问题的事情。我不确定是解析它的requirejs是触发它还是某些V8优化路径。我不认为Lint工具甚至建议反对这种做法所以它是相当模糊的,这是'#34;对"实践中的方式
答案 0 :(得分:5)
JavaScript中没有“关联数组”这样的东西。 [ 1, 2, 3 ]
是数组文字语法;它初始化一个数组。 { foo: "bar" }
是对象文字语法;它初始化一个Object。然而,JavaScript的一个怪癖是Arrays也恰好是Objects,这就是为什么这段代码“有用”的原因:
var data = [];
data["first"] = [];
data["first"]["second"] = [];
...但你不应该这样做,因为它没有任何意义。你正在初始化一个空数组([]
),但是你没有像使用数组那样使用它 - 你像对象一样使用它。如果您使用的属性名称(data["first"]
,相当于data.first
)而不是整数键(data[0]
),那么您希望使用Object。当你打算像对象一样使用它时,你不应该初始化一个数组。
根据经验,如果您需要每个项目都有名称,或者需要能够通过名称快速访问它们,请使用对象({}
)并使用字符串作为键。如果您需要能够按顺序迭代项目,请使用带整数的数组作为键。
我不知道您的内存不足错误的确切原因 - 尤其是在没有看到您的实际代码的情况下 - 但绝对是您应该使用对象({}
)的情况,而不是不使用整数键时的数组([]
)。 JavaScript引擎可以优化所有功能,Arrays和Objects也不例外,因此当您以引擎不期望的方式使用Array时可能会导致性能或内存问题,这并不奇怪。
P.S。作为一种风格问题,在处理对象时,请考虑使用属性表示法(或“点符号”,即foo.bar
)而不是下标符号(即foo["bar"]
):
var data = {};
data.first = {};
data.first.second = {};
data.first.second2 = "hello";
这与您发布的代码完全相同,但它更易于阅读,可能会帮助您记住对象和数组有不同的用途。您也可以将其表达为单个对象文字:
var data = {
first: {
second: {},
second2: "hello"
}
};
这也完全等效,可以帮助您查看对象的“结构”(只要您对缩进有规律)。
大多数JavaScript样式指南都说您应该始终使用“点表示法”,除非您的密钥会导致语法错误。例如,如果您有一个名为"foo/bar"
的属性,则显然无法执行此操作:
var obj.foo/bar = 1;
...因为这是一个语法错误。所以你必须这样做:
var obj["foo/bar"] = 1;
......这是完全有效的。这些情况往往是例外,所以我鼓励总是使用点表示法,除非你必须使用下标符号。