如何在JavaScript中创建一个高性能、不可变的数组?

4

假设我有一个经常使用的数组,比如一个单位矩阵,我想确保它不会被意外修改。如何创建一个不能被修改的JavaScript数组?

Object.freeze可能是我想要的,但jsperf报告称它比普通数组慢得多

编辑:标题指定性能是一个要求。我们需要进行大量的WebGL调用!

编辑2:更具体地说,这是为基于浏览器的WebGL游戏而设计的(因此需要性能),所以当前Chrome和Firefox的JavaScript解释器中可用的任何功能都可以使用。关于代码审查的社交压力,当然可以!但我们仍然是犯错误的人,偶尔会写错变量名。


2
所以每秒25-30,000,000次操作的速度还不够快。 - OJay
2
@OJay 正确。分配和随后的垃圾回收现在对我们来说是一个重要的性能成本。 - a paid nerd
1
你需要什么支持(我怀疑在当前的JS引擎中,没有比数组更快的东西了,但出于科学的利益,我愿意尝试 - 但如果需要在IE 9中工作,我不想建议使用代理)。 - Sean Vieira
1
有没有办法将数组作为私有变量隐藏在函数内部,这样它只能在其父函数内部进行更改,然后在父函数上创建接口暴露以供交互? - OJay
1
你需要创建一个身份矩阵的Array实例,并且只需创建一次,然后再将其冻结一次。听起来你是在寻找一个不必要的解决方案。 - PointedEars
显示剩余9条评论
1个回答

4
你可以手动设置数组的变异方法:
var mutatorMethods = ['fill', 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'];

function preventMutation() {
  throw new TypeError('Array is immutable');
}

function makeImmutableArray(origArray) {
  mutatorMethods.forEach(function(method) {
    origArray[method] = preventMutation;
  });

  return origArray;
}

var foo = makeImmutableArray(['foo', 'bar', 'baz']);

看这个 jsbin 作为例子:http://jsbin.com/zeser/1/edit 然而,这只是掩盖了一个更大的问题(考虑到仍可以通过直接索引修改数组foo[0] = 'froboz';):你为什么需要不可变的数组?
让我换个说法,你想要防止谁修改这个数组?如果这是公开面向外部的代码,建议你创建自己的对象并暴露出你所期望的使用方法。增加一层函数调用以进行迭代对性能几乎没有影响,并且它可以防止暴露有副作用的函数。
最后,如果你想要保护数组免受自己的修改,则会导致过分谨慎编码。这增加了复杂度。在我看来,防御编码更适用于来自系统外部的交互。防范自己的错误只会使代码更加复杂,因为你会不断地猜测自己,并且经常缺乏 if/else 检查的一致性。
相反,我建议你通过创建自己的 Array 对象来封装一个迭代对象的概念并使用它。

不会停止直接索引赋值,即foo [0] =“notfoo”。 - OJay
我认为在那个时候,你应该选择如何管理与不可变对象的接口。如果你想避免索引赋值,我建议不要使用数组,而是使用你自己的对象,它可以在闭包中隐藏一个数组。 - Sukima
它不仅聪明,而且在大多数浏览器中也表现出色(http://jsperf.com/freeze-array-performance/3)!(Firefox可能会从"use asm"代码中获得更好的里程,但我不会手写ASM,所以无法测试)。 - Sean Vieira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接