2013-07-03 12 views
10

Spędziłem trochę czasu szukając najlepszego sposobu na uniknięcie ciągu html i znalazłem kilka dyskusji na ten temat: discussion 1discussion 2. Prowadzi mnie to do funkcji replaceAll. Potem zrobiłem testy wydajności i próbowałem znaleźć rozwiązanie osiągające podobną prędkość bez powodzenia :(Co to jest zastępczy sekret wydajności? [Escape HTML]

Oto mój ostateczny test case set. Znalazłem go w sieci i rozwijam z moimi próbami (4 przypadki na dole) i nadal nie mogę osiągnąć replaceAll() . wydajność

Jaki jest sekret czarownica sprawia replaceAll() rozwiązanie tak szybkich

wita

fragmenty kodu?!

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); 
}; 

kredyty dla qwerty

najszybszy przypadku do tej pory:

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

Wiele zbudowany w metodach są realizowane w natywnym kodzie i wstępnych zoptymalizowanych (regexes jest jednym), naśladując ich w JavaScript w szybszy sposób jest po prostu trudny do zrobienia. –

+0

z pewnością, ale dlaczego "zastąp nowy przypadek RegExp" jest tak wolny. Używa również RegExp. – Saram

+0

nadal zamieniać bez regex wydaje się być szybsze http://jsperf.com/replaceallvssplitjoin –

Odpowiedz

4

Nareszcie znalazłem! Dzięki Jack za naprowadzenie mnie na konkretnym jsperf

Należy zauważyć, że wyniki badań są dziwne; gdy .replaceAll() jest zdefiniowany w Benchmark.prototype.setup działa dwa razy szybciej w porównaniu do kiedy jest zdefiniowany globalnie (tj. wewnątrz tagu). Nadal nie jestem pewien, dlaczego tak jest, ale na pewno musi być związany z , jak działa jsperf.

Odpowiedź brzmi:

replaceAll - tym REACH jsperf ograniczenie/błąd, spowodowany specjalnej sekwencji "\\$&", więc wyniki były złe.

- po wywołaniu bez argumentu zmienia definicję wyrażeń regularnych na /(?:). Nie wiem, czy to błąd, czy coś, ale wynik wydajności był fatalny po tym, jak został wywołany.

Oto mój result safe tests.

W końcu przygotowałem proper test cases.

Rezultatem jest to, że dla HTML uciec najlepszy sposób go używać ojczystego DOM oparte rozwiązania, jak:

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

lub jeśli go powtórzyć wiele razy można to zrobić ze zmiennymi raz przygotowane:

//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 
} 

Dziękuję wszystkim za współpracę & zamieszczanie komentarzy i wskazówek.

opis jsperf bug

String.prototype.replaceAll został zdefiniowany następująco:

function (str1, str2, ignore) { 
    return this.replace(new RegExp(str1.replace(repAll, "\\#{setup}"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$") : str2); 
} 
+1

Czy mógłbyś utworzyć link do błędu jsperf? –

+0

@Jack po uruchomieniu dowolnego testu buggy (gdzie replaceAll jest zdefiniowany w procedurze setup()) przejdź do konsoli i wyświetl treść 'String.prototype.replaceAll'. Zrobię jakiś komentarz, by odpowiedzieć za chwilę. – Saram

-1

Faktycznie istnieją szybsze sposoby, aby to zrobić.

Jeśli możesz zrobić podział w linii i dołączyć, uzyskasz lepszą wydajność.

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

Wypróbuj to i uruchom test wydajności.

+3

nie może potwierdzić: http://jsperf.com/htmlencoderegex/31 – Saram

+0

Sprawdź to http://dynamic-tools.net/toolbox/replaceAll/ – blganesh101

+0

Korzystanie z funkcji replaceAll: ** 0,054 ** sekund/ Korzystanie z funkcja replaceAll2: ** 0,106 ** sekund/ Podział/łączenie w linii: ** 0,1111 ** sekund/ Obiekt wyrównywania równoległego Obiekt: ** 0,182 ** sekunda Obiekt wyliczony w wierszu Obiekt bez łańcucha: ** 0,134 ** sekund – Saram

2

Jeśli chodzi o wydajność idzie, uważam, że funkcja poniżej jest tak dobry, jak to się robi:

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;'); 
    } 
}(); 

To inicjuje wyrażeń regularnych i zwraca zamknięcie, które faktycznie wykonuje zastępstwo.

Performance test

Należy zauważyć, że wyniki badań są dziwne; gdy .replaceAll() jest zdefiniowany wewnątrzBenchmark.prototype.setup działa dwa razy szybciej w porównaniu do tego, kiedy jest zdefiniowany globalnie (tj. wewnątrz znacznika <script>). Nadal nie jestem pewien, dlaczego tak jest, ale na pewno musi być związany z tym, jak działa jsperf.

Korzystanie RegExp.compile()

Chciałem uniknąć przy użyciu przestarzałej funkcji, głównie z powodu tego rodzaju świadczenia powinny być wykonywane automatycznie przez nowoczesnych przeglądarkach. Oto wersja z kompilowanymi wyrażeniami:

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;'); 
    } 
} 

To wszystko wysadzi z wody!

Performance test

Powodem .compile() daje taki wzrost wydajności jest, ponieważ podczas kompilacji globalną ekspresję, np /a/g zostanie przekonwertowany na /(?:)/ (w Chrome), co spowoduje, że będzie bezużyteczny.

Jeśli nie można wykonać kompilacji, przeglądarka powinna wygenerować błąd, zamiast go cicho niszczyć.

+0

Spójrz na @ Joachim Isaksson komentarz powyżej. Myślę, że znalazł lewę. – Saram

+0

@Saram Nie testowałem z przestarzałym '.compile()', ale wątpię, że byłaby użyteczna statystyka. Coś w testowym benchmarku wpływa na wyniki. –

+0

@Saram Ponadto, zgodnie z [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp), dosłowne wyrażenia regularne * są kompilowane *. –