JavaScript:有没有更好的方法来保留您的阵列,但有效地连接或替换项目?

时间:2013-10-30 02:38:04

标签: javascript performance

我正在寻找替换或添加数组元素而不删除原始引用的最佳方法。这是设置:

var a = [], b = [], c, i, obj;
for ( i = 0; i < 100000; i++ ) { a[ i ] = i; b[ i ] = 10000 - i; }
obj.data_list = a;

现在我们想连接b INTO a而不更改对a的引用,因为它在obj.data_list中使用。这是一种方法:

for ( i = 0; i < b.length; i++ ) { a.push( b[ i ] ); }

这似乎是一个有点terser和8x(在V8上)更快的方法:

a.splice.apply( a, [ a.length, 0 ].concat( b ) );

我发现这在迭代“就地”数组时很有用,并且不想在我去的时候触摸元素(一个好习惯)。我用初始参数启动一个新数组(让我们称之为keep_list),然后添加我希望保留的元素。最后,我使用此apply方法快速替换截断的数组:

var keep_list = [ 0, 0 ];
for ( i = 0; i < a.length; i++ ){ 
  if ( some_condition ){ keep_list.push( a[ i ] );
}

// truncate array
a.length = 0;

// And replace contents
a.splice.apply( a, keep_list );

此解决方案存在一些问题:

  • V8上的最大调用堆栈大小限制约为50k
  • 我还没有在其他JS引擎上测试过。
  • 这个解决方案有点神秘

有没有人找到更好的方法?

1 个答案:

答案 0 :(得分:1)

可能最有效的方法仍然是使用push

var source = [],
    newItems = [1, 2, 3];

source.push.apply(source, newItems);

如果达到最大调用堆栈,则可以将操作分成多个批次。

var source = [],
    newItems = new Array(500000),
    i = 0,
    len = newItems.length,
    batch;

for (; i < len; i++) newItems[i] = i;

//You need to find a real cross-browser stack size, here I just used 50k
while((batch = newItems.splice(0, 50000)).length) {
    source.push.apply(source, batch);
}

console.log(source[499999]);

另请注意,昂贵的操作可能会使浏览器挂起,尤其是在具有慢速JS引擎的旧浏览器中。为了解决这个问题,您可以进一步将流程拆分为较小的批次,并使用setTimeout让浏览器屏住呼吸。

最后我想到的另一种方法是在数组周围使用一个包装器对象,它允许你直接替换数组,因为你的引用将通过对象保留。

var arrWrapper = { list: [] },
    obj1 = { items: arrWrapper },
    obj2 = { items: arrWrapper };

//update the array
obj2.items.list = [1, 2, 3, 4];

//access the array
obj1.items.list;

唯一的限制是避免直接引用arrWrapper.list

注意:如果您仅定位现代浏览器,则可以使用WebWorkers。但据我所知,您只能传递序列化数据,这意味着工作人员无法直接修改源数组。