10

我花了一些时间寻找转义 html 字符串的最佳方法,并找到了一些关于此的讨论:讨论 1 讨论 2。它引导我替换所有功能。然后我进行了性能测试并试图找到实现类似速度但没有成功的解决方案:(

这是我的最终测试用例集。我在网上找到了它并通过我的尝试进行了扩展(底部有 4 个案例),但仍然无法达到replaceAll()性能。

什么是秘密女巫使replaceAll()解决如此迅速?

问候!

代码片段:

String.prototype.replaceAll = function(str1, str2, ignore) 
{
   return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
};

qwerty的学分

迄今为止最快的情况:

html.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll("'", '&#39;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
4

3 回答 3

4

终于我找到了!感谢Jack指出我关于 jsperf 的具体问题

我应该注意到测试结果很奇怪;当 .replaceAll() 在 Benchmark.prototype.setup 中定义时,它的运行速度是全局定义时(即在标签内)的两倍。我仍然不确定为什么会这样,但这肯定与 jsperf 本身的工作方式有关。

答案是:

replaceAll- 这达到了 jsperf 限制/错误,由特殊序列引起"\\$&",所以结果是错误的。

compile()- 当不带参数调用时,它会将正则表达式定义更改为/(?:). 我不知道是bug还是什么,但是调用后性能结果很糟糕。

这是我的结果安全测试

最后我准备了适当的测试用例

结果是,对于 HTML 转义,最好的方法是使用基于原生 DOM 的解决方案,例如:

document.createElement('div').appendChild(document.createTextNode(html)).parentNode.innerHTML

或者,如果您重复多次,您可以使用已准备好的变量来执行此操作:

//prepare variables
var DOMtext = document.createTextNode("test");
var DOMnative = document.createElement("span");
DOMnative.appendChild(DOMtext);

//main work for each case
function HTMLescape(html){
  DOMtext.nodeValue = html;
  return DOMnative.innerHTML
}

感谢大家的合作并发表评论和指示。

jsperf 错误描述

String.prototype.replaceAll定义如下:

function (str1, str2, ignore) {
  return this.replace(new RegExp(str1.replace(repAll, "\\#{setup}"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$") : str2);
}
于 2013-07-03T14:03:18.167 回答
2

就性能而言,我发现下面的功能是最好的:

String.prototype.htmlEscape = function() {
    var amp_re = /&/g, sq_re = /'/g, quot_re = /"/g, lt_re = /</g, gt_re = />/g;

    return function() {
        return this
          .replace(amp_re, '&amp;')
          .replace(sq_re, '&#39;')
          .replace(quot_re, '&quot;')
          .replace(lt_re, '&lt;')
          .replace(gt_re, '&gt;');
    }
}();

它初始化正则表达式并返回一个实际执行替换的闭包。

性能测试

我应该注意到测试结果很奇怪;当在内部.replaceAll()定义时,它的运行速度是全局定义时(即在标签内)的两倍。我仍然不确定为什么会这样,但这肯定与 jsperf 本身的工作方式有关。 Benchmark.prototype.setup<script>

使用RegExp.compile()

我想避免使用已弃用的函数,主要是因为这种性能应该由现代浏览器自动完成。这是一个带有编译表达式的版本:

String.prototype.htmlEscape2 = function() {
    var amp_re = /&/g, sq_re = /'/g, quot_re = /"/g, lt_re = /</g, gt_re = />/g;

    if (RegExp.prototype.compile) {
        amp_re.compile();
        sq_re.compile();
        quot_re.compile();
        lt_re.compile();
        gt_re.compile();
    }

    return function() {
        return this
          .replace(amp_re, '&amp;')
          .replace(sq_re, '&#39;')
          .replace(quot_re, '&quot;')
          .replace(lt_re, '&lt;')
          .replace(gt_re, '&gt;');
    }
}

这样做会将其他所有东西都从水中吹走!

性能测试

之所以.compile()会提高性能,是因为当您编译全局表达式时,例如/a/g,它会被转换为/(?:)/(在 Chrome 上),这会使它变得无用。

如果编译不能完成,浏览器应该抛出一个错误而不是默默地销毁它。

于 2013-07-03T09:57:13.753 回答
-1

实际上有更快的方法来做到这一点。

如果您可以进行内联拆分和连接,您将获得更好的性能。

//example below
var test = "This is a test string";
var test2 = test.split("a").join("A");

试试这个并运行性能测试。

于 2013-07-03T07:37:13.840 回答