3

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

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

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

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

这似乎是一种更简洁和 8 倍(在 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 引擎上测试过。
  • 这个解决方案有点神秘

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

4

1 回答 1

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. 但是据我所知,您只能传递序列化数据,这意味着工作人员无法直接修改源数组。

于 2013-10-30T12:51:27.220 回答