Чем заменить весь секрет производительности? [HTML escape]
Я потратил некоторое время на поиски лучшего способа избежать 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('&', '&').replaceAll('"', '"').replaceAll("'", ''').replaceAll('<', '<').replaceAll('>', '>');
3 ответа
Наконец я нашел это! Спасибо, Ja͢ck, за то, что указал мне на jsperf
Должен отметить, что результаты теста странные; Когда.replaceAll() определен внутри Benchmark.prototype.setup, он выполняется в два раза быстрее по сравнению с глобальным определением (т. е. внутри тега). Я до сих пор не уверен, почему это так, но это определенно должно быть связано с тем, как работает сам jsperf.
Ответ:
replaceAll
- это достижение лимита / ошибки jsperf, вызванное специальной последовательностью "\\$&"
, так что результаты были неправильными.
compile()
- когда вызывается без аргумента, он меняет определение регулярного выражения на /(?:)
, Я не знаю, если это ошибка или что-то, но результат производительности был дрянной после того, как он был вызван.
Вот мой результат безопасных испытаний.
Наконец я подготовил надлежащие тесты.
В результате для 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);
}
Что касается производительности, я обнаружил, что приведенная ниже функция хороша:
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, '&')
.replace(sq_re, ''')
.replace(quot_re, '"')
.replace(lt_re, '<')
.replace(gt_re, '>');
}
}();
Он инициализирует регулярные выражения и возвращает замыкание, которое фактически выполняет замену.
Должен отметить, что результаты теста странные; когда .replaceAll()
определяется внутри Benchmark.prototype.setup
он работает в два раза быстрее по сравнению с глобальным определением (т. е. внутри <script>
тег). Я до сих пор не уверен, почему это так, но это определенно должно быть связано с тем, как работает сам jsperf.
С помощью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, '&')
.replace(sq_re, ''')
.replace(quot_re, '"')
.replace(lt_re, '<')
.replace(gt_re, '>');
}
}
Все это выдувает все остальное из воды!
Причина по которой .compile()
дает такой прирост производительности, потому что, когда вы компилируете глобальное выражение, например, /a/g
это превращается в /(?:)/
(на Chrome), что делает его бесполезным.
Если компиляция не может быть выполнена, браузер должен выдать ошибку, а не молча уничтожать ее.
На самом деле есть более быстрые способы сделать это.
Если бы вы могли выполнить встроенное разделение и присоединение, вы получите лучшую производительность.
//example below
var test = "This is a test string";
var test2 = test.split("a").join("A");
Попробуйте это и запустите тест производительности.