如何强制JavaScript深度复制字符串?


71

我有一些如下的javascript代码:

var myClass = {
  ids: {}
  myFunc: function(huge_string) {
     var id = huge_string.substr(0,2);
     ids[id] = true;
  }
}

稍后,该函数将使用一些大字符串(100 MB +)进行调用。我只想保存在每个字符串中找到的简短ID。但是,谷歌浏览器的子字符串函数(实际上是我的代码中的正则表达式)仅返回引用原始字符串的“切片字符串”对象。因此,在对进行一系列调用之后myFunc,我的chrome标签页内存不足,因为临时huge_string对象无法进行垃圾回收。

如何复制字符串,id以便huge_string不维护对的引用,并且huge_string可以对垃圾进行垃圾回收?

在此处输入图片说明


1
“子字符串函数(实际上是我的代码中的正则表达式)仅返回“切片的字符串”对象,该对象引用了原始对象” -嗯?.substr().substring().slice(),以及相关的正则表达式函数都返回一个新的字符串。调用的其他代码是否myClass.myFunc()保留了对您的巨大字符串的引用?如果您的实际代码更复杂,是否会意外地将大字符串保留在闭包中?
nnnnnn 2015年

2
@nnnnnn无法从JavaScript判断它是否为“新”字符串数据。实现可以共享基础数据,而不会违反ECMAScript的任何部分。Firefox有六种不同的字符串实现(特别是JSDependentString),如果Chrome具有类似的优化功能(在某些情况下可能表现不理想),我并不感到惊讶。话虽如此..如果是鲱鱼,我不会感到惊讶。
user2864740


4
错误报告#2869包含解决方法:(' ' + src).slice(1)。没有官方决议。
user2864740

1
我在将脚本转换为“严格使用”时遇到了这种情况。在这里,我们写了一个现在只读的字符串文字,并得到了“无法分配为只读字符串的'0'属性”。
亚历克斯·迪克森

Answers:


71

JavaScript对ECMAScript的实现可能因浏览器而异,但是对于Chrome,许多字符串操作(substr,slice,regex等)仅保留对原始字符串的引用,而不是复制该字符串。这是Chrome中的一个已知问题(错误#2869)。要强制复制字符串,可以使用以下代码:

var string_copy = (' ' + original_string).slice(1);

该代码通过在字符串的前面附加一个空格来工作。此串联会在Chrome的实现中产生一个字符串副本。然后可以引用空格后的子字符串。

解决方案的问题已在此处重新创建:http : //jsfiddle.net/ouvv4kbs/1/

警告:加载时间很长,请打开Chrome调试控制台以查看进度打印输出。

// We would expect this program to use ~1 MB of memory, however taking
// a Heap Snapshot will show that this program uses ~100 MB of memory.
// If the processed data size is increased to ~1 GB, the Chrome tab
// will crash due to running out of memory.

function randomString(length) {
  var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  var result = '';
  for (var i = 0; i < length; i++) {
    result +=
        alphabet[Math.round(Math.random() * (alphabet.length - 1))];
  }
  return result;
};

var substrings = [];
var extractSubstring = function(huge_string) {
  var substring = huge_string.substr(0, 100 * 1000 /* 100 KB */);
  // Uncommenting this line will force a copy of the string and allow
  // the unused memory to be garbage collected
  // substring = (' ' + substring).slice(1);
  substrings.push(substring);
};

// Process 100 MB of data, but only keep 1 MB.
for (var i =  0; i < 10; i++) {
  console.log(10 * (i + 1) + 'MB processed');
  var huge_string = randomString(10 * 1000 * 1000 /* 10 MB */);
  extractSubstring(huge_string);
}

// Do something which will keep a reference to substrings around and
// prevent it from being garbage collected.
setInterval(function() {
  var i = Math.round(Math.random() * (substrings.length - 1));
  document.body.innerHTML = substrings[i].substr(0, 10);
}, 2000);

在此处输入图片说明


var string_copy = original_string.slice(0);
韦斯利史丹姆

@WesleyStam我认为AffluentOwl的帖子之所以起作用,是因为他在字符串前添加了一个字符,这会导致字符串被复制,因为slice运算符实际上并未像应有的那样复制字符串。
乔兰·多克斯

18

我将Object.assign()方法用于字符串,对象,数组等:

const newStr = Object.assign("", myStr);
const newObj = Object.assign({}, myObj);
const newArr = Object.assign([], myArr);

请注意,Object.assign仅将键及其属性值复制到一个对象内(仅限一级)。要深度克隆嵌套对象,请参考以下示例:

let obj100 = { a:0, b:{ c:0 } };
let obj200 = JSON.parse(JSON.stringify(obj100));
obj100.a = 99; obj100.b.c = 99; // No effect on obj200

1
它看起来不像预期的结果:i.stack.imgur.com/1hsxF.png
Qwertiy

当我这样做时Object.assign("", "abc");,我得到一个空的String对象。
425nesp

1
const newStr = Object.assign("", myStr); console.log(newStr);这将打印数组:[String: ''] {'0': 'H','1': 'e',...}]。不幸的是不适用于字符串复制。
乔治·米洛纳斯

我最喜欢此解决方案的外观,但不幸的是,它不适用于Chrome浏览器,我求助于“黑客”外观的字符串复制和切片解决方案
arhnee

11

不确定如何测试,但是使用字符串插值创建新的字符串变量可以工作吗?

newString = `${oldString}`

@Qwertiy为什么您说这不起作用?似乎为我工作。从上面运行命令后,我进行了更改,oldString并且它没有更改newString。另外,typeof两者都返回原始字符串类型。
425nesp

9

您可以使用:

 String.prototype.repeat(1) 

看来运作良好。请参阅的MDN文档repeat


var a = "hi"; var b = a.repeat(1);为我工作。我尝试改变ab保持不变。
425nesp

最简单的解决方案
二进制

在我的测试中,Chromerepeat当前实际上并未在其上进行复制。
Patrick Linskey

3

推入阵列时遇到问题。每个条目都将以相同的字符串结尾,因为它引用的对象值随着我通过.next()函数对结果进行迭代而发生了变化。这是允许我复制字符串并在数组结果中获得唯一值的原因:

while (results.next()) {
  var locationName = String(results.name);
  myArray.push(locationName);
}

0

我通常使用strCopy = new String (originalStr); 由于某种原因不建议这样做吗?


尝试在上面运行typeof。它为您提供了String类型的实例,而不是String原语,后者提供了更多功能。话虽如此,将其作为类似功能运行strCopy = String(originalStr);可能会起作用。参考文献:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
列维穆尼斯

另外,只需对其进行测试,strCopy = String(originalStr);然后尝试通过做修改原始字符串strCopy[0] = "X"。这两个副本将被修改。
Levi Muniz

0

我遇到了这个问题,这就是我应付的方式:

let copy_string = [];
copy_string.splice(0, 0, str);

我相信这会将str深复制到copy_string。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.