Czy JavaScript ma wbudowaną klasę stringbuilder?

Odpowiedzi:

319

Jeśli musisz napisać kod dla Internet Explorera, upewnij się, że wybrałeś implementację, która używa sprzężeń tablicowych. Łączenie łańcuchów z operatorem +lub +=jest wyjątkowo wolne w IE. Jest to szczególnie prawdziwe w przypadku IE6. W nowoczesnych przeglądarkach+= jest zwykle tak samo szybkie, jak sprzężenia tablic.

Kiedy muszę wykonać wiele konkatenacji ciągów, zwykle wypełniam tablicę i nie używam klasy konstruktora ciągów:

var html = [];
html.push(
  "<html>",
  "<body>",
  "bla bla bla",
  "</body>",
  "</html>"
);
return html.join("");

Zauważ, że pushmetody akceptują wiele argumentów.

Fabian Jakobs
źródło
7
A jeśli generujesz dane wyjściowe bezpośrednio lub wszyscy członkowie są literałami, to też [foo(), "bar", "baz"].join("");działa.
Anonimowy
1
Chociaż prawdopodobnie nie można oczekiwać, że linki Dropbox będą działać przez prawie 3 lata, byłbym ciekawy tego porównania - i jeśli nadal się utrzymuje.
Cornelius
1
@DaveWard, twój link jest zepsuty :(
Ivan Kochurkin
Uważam, że jest to o wiele bardziej czytelne niż ten ciąg + ciąg + ciąg
Andrew Ehrlich
12
Nie wiedziałem, że mogę pushzaakceptować wiele argumentów. Losowe rzeczy, których się uczysz.
Carcigenicate
55

Właśnie sprawdziłem wydajność na http://jsperf.com/javascript-concat-vs-join/2 . Przypadki testowe konkatenują lub dołączają do alfabetu 1000 razy.

W obecnych przeglądarkach (FF, Opera, IE11, Chrome) „concat” jest około 4-10 razy szybszy niż „dołącz”.

W IE8 oba zwracają prawie równe wyniki.

W IE7 „dołącz” jest niestety około 100 razy szybszy.

Andreas
źródło
3
Dzięki za to. To powinno zostać powiększone na liście odpowiedzi. Jest także znacznie szybszy na IE10 (wiem, że to nie jest nowoczesna przeglądarka, ale wspominam o tym wszystkim potencjalnym programistom NMCI, którzy to widzą).
James Wilson,
@Andreas Uważam, że Twój test uderza w ścieżkę kodu w Chrome, gdzie nigdy nie robi rzeczywistego konkatenacji, ponieważ ciąg nigdy nie jest czytany. Nawet przy wymuszaniu tego, szybkość wykonania jest wciąż znacznie wyższa
Joseph Lennox
37

Nie, nie ma wbudowanej obsługi ciągów znaków. Zamiast tego musisz użyć konkatenacji.

Możesz oczywiście utworzyć tablicę różnych części łańcucha, a następnie wywołać join() tę tablicę, ale zależy to od tego, w jaki sposób sprzężenie jest implementowane w używanym interpretera JavaScript.

Zrobiłem eksperyment, aby porównać szybkość str1+str2metody z array.push(str1, str2).join()metodą. Kod był prosty:

var iIterations =800000;
var d1 = (new Date()).valueOf();
str1 = "";
for (var i = 0; i<iIterations; i++)
    str1 = str1 + Math.random().toString();
var d2 = (new Date()).valueOf();
log("Time (strings): " + (d2-d1));

var d3 = (new Date()).valueOf();
arr1 = [];
for (var i = 0; i<iIterations; i++)
    arr1.push(Math.random().toString());
var str2 = arr1.join("");
var d4 = (new Date()).valueOf();
log("Time (arrays): " + (d4-d3));

Przetestowałem to w Internet Explorer 8 i Firefox 3.5.5, oba na Windows 7 x64.

Na początku testowałem na małej liczbie iteracji (około stu, około tysiąca pozycji). Wyniki były nieprzewidywalne (czasem konkatenacja łańcuchów trwała 0 milisekund, czasem 16 milisekund, to samo przy łączeniu tablic).

Gdy zwiększyłem tę liczbę do 50 000, wyniki były różne w różnych przeglądarkach - w Internet Explorerze łączenie ciągów było szybsze (94 milisekundy), a łączenie było wolniejsze (125 milisekund), podczas gdy w Firefoksie łączenie tablic było szybsze (113 milisekund) niż łączenie ciągów (117 milisekund).

Potem zwiększyłem liczbę do 500 000. Teraz array.join()było wolniej niż konkatenacja łańcuchów w obu przeglądarkach: konkatenacja łańcuchów wynosiła 937 ms w przeglądarce Internet Explorer, 1155 ms w przeglądarce Firefox, przyłączenie tablicy 1265 w przeglądarce Internet Explorer i 1207 ms w przeglądarce Firefox.

Maksymalna liczba iteracji, które mogłam przetestować w Internet Explorerze bez „wykonywania skryptu zbyt długo” wynosiła 850 000. Następnie Internet Explorer był 1593 dla łączenia łańcuchów i 2046 dla łączenia tablic, a Firefox miał 2101 dla łączenia łańcuchów i 2249 dla łączenia tablic.

Wyniki - jeśli liczba iteracji jest niewielka, możesz spróbować użyć array.join(), ponieważ może być szybsza w Firefoksie. Gdy liczba rośnie, string1+string2metoda jest szybsza.

AKTUALIZACJA

Test wykonałem na Internet Explorerze 6 (Windows XP). Proces przestał odpowiadać natychmiast i nigdy się nie zakończył, jeśli spróbowałem testu na ponad 100 000 iteracjach. Przy 40 000 iteracjach wyniki były

Time (strings): 59175 ms
Time (arrays): 220 ms

Oznacza to - jeśli chcesz obsługiwać Internet Explorera 6, wybierz, array.join()który jest znacznie szybszy niż konkatenacja ciągów.

naiwiści
źródło
join()jest częścią ECMAScript i afaik każdy interpreter JavaScript go implementuje. Dlaczego miałoby to zależeć?
Eli Gray
miał na myśli JAK to zostało zaimplementowane ... jeśli jest zaimplementowane w taki sposób, że w pętli ciąg jest ciągle dołączany, a nie tworzony naraz, wtedy użycie złączenia byłoby bezcelowe
Jan
Tak, właśnie to miałem na myśli. Przepraszam za mój angielski ;-) Dodałem wyniki porównania, jak szybko która metoda działa w dwóch przeglądarkach. Widać, jest inaczej.
naiwistów
2
IE6, jak zawsze, jest wyjątkiem :)
Gordon Tucker
10
Ludzie z IE6 są przyzwyczajeni do tego, że wszystko jest naprawdę wolne. Nie sądzę, żeby cię obwinili.
Lodewijk
8

Ten kod wygląda jak trasa, którą chcesz obrać z kilkoma zmianami.

Będziesz chciał zmienić metodę dołączania, aby wyglądała tak. Zmieniłem go, aby zaakceptował cyfrę 0 i sprawił, że zwróci, thisaby można było łączyć swoje załączniki.

StringBuilder.prototype.append = function (value) {
    if (value || value === 0) {
        this.strings.push(value);
    }
    return this;
}
Gordon Tucker
źródło
Dlaczego akceptują tylko liczby inne niż NaN i niepuste ciągi znaków? Twoja metoda nie zaakceptuje null, false, puste struny, undefinedalbo NaN.
Eli Gray,
@Elijah - Wolę utrzymywać klasę StringBuilder w czystości, nie akceptując niczego oprócz poprawnych ciągów i liczb. To tylko osobiste preferencje.
Gordon Tucker
5

Wersja ECMAScript 6 (znana również jako ECMAScript 2015) JavaScript wprowadziła literały łańcuchowe .

var classType = "stringbuilder";
var q = `Does JavaScript have a built-in ${classType} class?`;

Zauważ, że tyknięcia zamiast pojedynczych cudzysłowów otaczają ciąg.

Theophilus
źródło
17
Jak to odpowiada na pytanie?
Peter Mortensen
@Peter Mortensen, ta odpowiedź daje jedynie inny sposób na zbudowanie łańcucha. Oryginalny plakat nie określał, jakiego rodzaju funkcjonalność kreatora ciągów ma być poszukiwana.
Theophilus
1
To nie odpowiada na pytanie. W ogóle.
Massimiliano Kraus,
2

W C # możesz zrobić coś takiego

 String.Format("hello {0}, your age is {1}.",  "John",  29) 

W JavaScript możesz zrobić coś takiego

 var x = "hello {0}, your age is {1}";
 x = x.replace(/\{0\}/g, "John");
 x = x.replace(/\{1\}/g, 29);
Sporty
źródło
2
Bardzo wątpię, aby użycie wyrażenia regularnego zamiast łączenia ciągów było bardziej wydajne
tic
Co więcej, jest to okropne wdrożenie. Zepsuje się, jeśli ciąg, który {0}zostanie zastąpiony, zawiera {1}.
ikegami,
@ikegami ciąg nie jest zmienną, jest stałą, więc wiesz, co zawierało a priori.
sport
@sports, kopiowanie i wklejanie tego wszystkiego w kodzie to jeszcze gorszy pomysł.
ikegami
Jedna linijka z 1 $ i 2 $ zastępuje grupy nie przechwytujące: x..replace (/ ([\ s \ S] *?) \ {0 \} ([\ s \ S] *?) \ {1 \} / g, „1 USD za 225 USD”)
T.CK
1

Dla zainteresowanych, oto alternatywa dla wywołania Array.join:

var arrayOfStrings = ['foo', 'bar'];
var result = String.concat.apply(null, arrayOfStrings);
console.log(result);

Dane wyjściowe, zgodnie z oczekiwaniami, to ciąg „foobar”. W Firefoksie takie podejście przewyższa Array.join, ale jest lepsze niż konkatenacja. Ponieważ String.concat wymaga, aby każdy segment był określony jako osobny argument, obiekt wywołujący jest ograniczony przez dowolny limit liczby argumentów narzucony przez silnik JavaScript. Więcej informacji można znaleźć w dokumentacji Function.prototype.apply () .

Aaron
źródło
Nie działa to w Chrome, ponieważ „String.concat” jest niezdefiniowany. Zamiast tego możesz użyć '' .concat.apply ('', arrayOfStrings). Ale to wciąż bardzo powolna metoda.
Andreas
1

Zdefiniowałem tę funkcję:

function format() {
        var args = arguments;
        if (args.length <= 1) { 
            return args;
        }
        var result = args[0];
        for (var i = 1; i < args.length; i++) {
            result = result.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), args[i]);
        }
        return result;
    }

I można go nazwać jak c #:

 var text = format("hello {0}, your age is {1}.",  "John",  29);

Wynik:

cześć John, masz 29 lat.

TotPeRo
źródło
1
Podoba mi się ... wygląda jak c #
Ayo Adesina
2
Ta odpowiedź nie ma nic wspólnego z pytaniem.
Massimiliano Kraus
0

Kiedy widzę, że robię dużo łączenia łańcuchów w JavaScript, zaczynam szukać szablonów. Handlebars.js działa całkiem dobrze, utrzymując HTML i JavaScript bardziej czytelnymi.http://handlebarsjs.com

początek
źródło