将 HTML 标记转义为 HTML 实体的最快方法?

我正在编写一个 Chrome 扩展,它包含了以下工作的 很多: 通过将 <>&分别转换为 &lt;&gt;&amp;,对包含 HTML 标签的 也许吧字符串进行消毒。

(In other words, the same as PHP's htmlspecialchars(str, ENT_NOQUOTES) – I don't think there's any real need to convert double-quote characters.)

这是我迄今为止发现的最快的函数:

function safe_tags(str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') ;
}

但是,当我需要一次运行几千个字符串时,仍然存在很大的延迟。

有人能改进一下吗? 它主要针对10到150个字符之间的字符串,如果这样做有什么不同的话。

(我的一个想法是,不必费心去编码大于号——这样做真的会有危险吗?)

170685 次浏览

您可以尝试传递一个回调函数来执行替换:

var tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};


function replaceTag(tag) {
return tagsToReplace[tag] || tag;
}


function safe_tags_replace(str) {
return str.replace(/[&<>]/g, replaceTag);
}

下面是一个性能测试: http://jsperf.com/encode-html-entities与重复调用 replace函数以及使用 Dmitrij 提出的 DOM 方法进行比较。

你的方法似乎更快..。

但你为什么需要它?

你可以这样做:

var escape = document.createElement('textarea');
function escapeHTML(html) {
escape.textContent = html;
return escape.innerHTML;
}


function unescapeHTML(html) {
escape.innerHTML = html;
return escape.textContent;
}

这是小样。

All-in-one script:

// HTML entities Encode/Decode


function htmlspecialchars(str) {
var map = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"'": "&#39;" // ' -> &apos; for XML only
};
return str.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function htmlspecialchars_decode(str) {
var map = {
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
"&quot;": "\"",
"&#39;": "'"
};
return str.replace(/(&amp;|&lt;|&gt;|&quot;|&#39;)/g, function(m) { return map[m]; });
}
function htmlentities(str) {
var textarea = document.createElement("textarea");
textarea.innerHTML = str;
return textarea.innerHTML;
}
function htmlentities_decode(str) {
var textarea = document.createElement("textarea");
textarea.innerHTML = str;
return textarea.value;
}

http://pastebin.com/JGCVs0Ts

马丁的方法作为一个原型函数:

String.prototype.escape = function() {
var tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;'
};
return this.replace(/[&<>]/g, function(tag) {
return tagsToReplace[tag] || tag;
});
};


var a = "<abc>";
var b = a.escape(); // "&lt;abc&gt;"

Martijn 的方法作为处理 "标记(在 javascript 中使用)的单一函数:

function escapeHTML(html) {
var fn=function(tag) {
var charsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;'
};
return charsToReplace[tag] || tag;
}
return html.replace(/[&<>"]/g, fn);
}

The AngularJS source code also has a version inside of 角消毒.

var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}

最快的方法是:

function escapeHTML(html) {
return document.createElement('div').appendChild(document.createTextNode(html)).parentNode.innerHTML;
}

这种方法比基于“替换”的方法快大约两倍,参见 http://jsperf.com/htmlencoderegex/35

资料来源: https://stackoverflow.com/a/17546215/698168

function encode(r) {
return r.replace(/[\x26\x0A\x3c\x3e\x22\x27]/g, function(r) {
return "&#" + r.charCodeAt(0) + ";";
});
}


test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');


/*
\x26 is &ampersand (it has to be first),
\x0A is newline,
\x22 is ",
\x27 is ',
\x3c is <,
\x3e is >
*/
<textarea id=test rows=11 cols=55>www.WHAK.com</textarea>

节目有点晚了,但是使用 encodeURIComponent()decodeURIComponent()有什么问题吗?

我对速度不是很确定,但是如果你想要简单,我建议使用 loash/下划线 逃跑函数。

一个更快/更短的解决方案是:

escaped = new Option(html).innerHTML

This is related to some weird vestige of JavaScript whereby the Option element retains a constructor that does this sort of escaping automatically.

归功于 https://github.com/jasonmoo/t.js/blob/master/t.js

我会把 XMLSerializer加进去。它在不使用任何对象缓存(不在序列化程序上,也不在 Text 节点上)的情况下提供最快的结果。

function serializeTextNode(text) {
return new XMLSerializer().serializeToString(document.createTextNode(text));
}

额外的好处是它支持与文本节点不同的序列化属性:

function serializeAttributeValue(value) {
const attr = document.createAttribute('a');
attr.value = value;
return new XMLSerializer().serializeToString(attr);
}

通过检查规范,您可以看到它实际上替换了什么,对于 文本节点属性值都是如此。完整的文档有更多的节点类型,但概念是相同的。

至于性能,没有缓存时它是最快的。如果允许缓存,那么对具有子 Text 节点的 HTMLElement 调用 innerHTML是最快的。正则表达式是最慢的(其他评论证明了这一点)。当然,XMLSerializer 在其他浏览器上可能更快,但在我(有限的)测试中,innerHTML是最快的。


最快单行:

new XMLSerializer().serializeToString(document.createTextNode(text));

缓存速度最快:

const cachedElementParent = document.createElement('div');
const cachedChildTextNode = document.createTextNode('');
cachedElementParent.appendChild(cachedChildTextNode);


function serializeTextNode(text) {
cachedChildTextNode.nodeValue = text;
return cachedElementParent.innerHTML;
}

Https://jsperf.com/htmlentityencode/1