我有一些看起来像这样的 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
进行垃圾收集?
https://i.stack.imgur.com/pzJai.png
.substr()
、.substring()
、.slice()
和相关的正则表达式函数都返回一个 new 字符串。调用 myClass.myFunc()
的其他代码是否保留了对您的大字符串的引用?如果您的真实代码更复杂,是否会意外地将巨大的字符串保留在闭包中?
(' ' + src).slice(1)
。没有官方决议。
JavaScript 对 ECMAScript 的实现可能因浏览器而异,但是对于 Chrome,许多字符串操作(substr、slice、regex 等)只是保留对原始字符串的引用,而不是复制字符串。这是 Chrome (Bug #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);
https://i.stack.imgur.com/obsf1.png
不确定如何测试,但使用字符串插值创建新的字符串变量是否有效?
newString = `${oldString}`
oldString
并没有更改 newString。此外,typeof
为两者返回了原始字符串类型。
!function () { const outputArr = []; const chars = 'ABC'; while(outputArr.length < 4000) { outputArr.push( chars[Math.floor(Math.random() * chars.length)])} const output = outputArr.join(''); console.time('interpolation'); const newVariable = `${output}`; console.timeEnd('interpolation'); }();
您可以使用:
String.prototype.repeat(1)
它似乎运作良好。请参阅 MDN documentation on repeat
。
var a = "hi"; var b = a.repeat(1);
适合我。我尝试更改 a
并且 b
保持不变。
repeat
上制作副本。
我对字符串、对象、数组等使用 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
Object.assign("", "abc");
时,我得到一个空的 String 对象。
const newStr = Object.assign("", myStr); console.log(newStr);
这将打印一个数组:[String: ''] {'0': 'H','1': 'e',...}]
。不幸的是不适用于字符串复制。
在这里看到一些回复很有趣。如果您不担心旧版浏览器支持 (IE6+),请直接跳到插值方法,因为它非常高效。
一种最向后兼容(回到 IE6),并且仍然非常高效的按值复制字符串的方法是将其拆分为一个新数组并立即重新加入该新数组作为字符串:
let str = 'abc';
let copiedStr = str.split('').join('');
console.log('copiedStr', copiedStr);
在幕后
上面所做的是调用 JavaScript 以不使用任何字符作为分隔符来拆分字符串,这会将每个单独的字符拆分为新创建的数组中自己的元素。这意味着,在短时间内,copiedStr
变量如下所示:
['a', 'b', 'c']
然后,立即重新连接 copiedStr
变量,在每个元素之间不使用任何字符作为分隔符,这意味着新创建的数组中的每个元素都被推回到一个全新的字符串中,从而有效地复制了该字符串。
在执行结束时,copiedStr
是它自己的变量,它输出到控制台:
abc
表现
平均而言,这在我的机器上大约需要 0.007 毫秒 - 0.01 毫秒,但您的里程可能会有所不同。在 4,000 个字符的字符串上进行测试,该方法复制字符串的最大时间为 0.2 毫秒,平均约为 0.14 毫秒,因此它仍然具有可靠的性能。
无论如何,谁在乎旧版支持?/插值方法
但是,如果您不担心旧版浏览器支持,那么 Pirijan 在此处的一个答案中提供的 interpolation
方法是一种非常高效的方法并且易于复制字符串:
let str = 'abc';
let copiedStr = `${str}`;
在相同的 4,000 个字符长度的字符串上测试 interpolation
的性能,我看到平均为 0.004 毫秒,最大值为 0.1 毫秒,最小值为惊人的 0.001 毫秒(非常频繁)。
推入阵列时遇到问题。每个条目都会以相同的字符串结束,因为它引用了一个对象上的值,该值在我通过 .next() 函数迭代结果时发生了变化。这是允许我复制字符串并在我的数组结果中获取唯一值的原因:
while (results.next()) {
var locationName = String(results.name);
myArray.push(locationName);
}
使用 String.slice()
const str = '快速的棕色狐狸跳过懒狗。'; // 在不修改原始字符串的情况下创建一个新字符串 const new_str = str.slice();控制台.log(new_str);
我通常使用 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"
修改原始字符串。两个副本都将被修改。
在我看来,这是最干净和最自我记录的解决方案:
const strClone = String(strOrigin);
我遇到了这个问题,这就是我处理它的方式:
let copy_string = [];
copy_string.splice(0, 0, str);
我相信这会将 str 深度复制到 copy_string。
str
变量将被推入 copy_string
,但 copy_string
是一个数组,因此您必须使用以下内容完成此操作:const copiedVariable = copy_string.join('')
将数组重新组合成一个字符串。