Odpowiednik String.format w jQuery

194

Próbuję przenieść kod JavaScript z MicrosoftAjax do JQuery. Używam ekwiwalentów JavaScript w MicrosoftAjax popularnych metod .net, np. String.format (), String.startsWith () itp. Czy są w JQuery ich odpowiedniki?

Waleed Eissa
źródło

Odpowiedzi:

193

The Kod źródłowy dla ASP.NET AJAX jest dostępne dla odniesienia, dzięki czemu można podnieść przez to i zawierać elementy, które chcesz dalej używać w oddzielnym pliku JS. Lub możesz przenieść je do jQuery.

Oto funkcja formatu ...

String.format = function() {
  var s = arguments[0];
  for (var i = 0; i < arguments.length - 1; i++) {       
    var reg = new RegExp("\\{" + i + "\\}", "gm");             
    s = s.replace(reg, arguments[i + 1]);
  }

  return s;
}

A oto cele i funkcje prototypowe ...

String.prototype.endsWith = function (suffix) {
  return (this.substr(this.length - suffix.length) === suffix);
}

String.prototype.startsWith = function(prefix) {
  return (this.substr(0, prefix.length) === prefix);
}
Josh Stodola
źródło
2
Nie wygląda na to, żeby było w tym wiele. Wersja JavaScript nie zawiera oczywiście wszystkich elementów do fantazyjnego formatowania liczb. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna
Wow, właściwie już o tym myślałem, ale także myślałem, że to nie jest możliwe z powodu licencji, nie wiedziałem, że wydali ją na Microsoft Permissive License, wielkie dzięki za to
Waleed Eissa
23
Licencja lub brak licencji .. jest tylko jeden właściwy sposób na napisanie czegoś tak prostego
adamJLev
1
Konstruowanie (a następnie odrzucanie) obiektu RegEx dla każdego argumentu za każdym razem, gdy wywoływany jest format, może przeciążać śmietnik.
mckoss
14
Ostrzeżenie: To sformatuje rekurencyjnie: więc jeśli tak {0}{1}, {0}najpierw zostanie zamieniony, a następnie wszystkie wystąpienia {1}zarówno już podstawionego tekstu, jak i oryginalnego formatu zostaną zastąpione.
Zenexer
147

Jest to szybsza / prostsza (i prototypowa) odmiana funkcji, którą opublikował Josh:

String.prototype.format = String.prototype.f = function() {
    var s = this,
        i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};

Stosowanie:

'Added {0} by {1} to your collection'.f(title, artist)
'Your balance is {0} USD'.f(77.7) 

Używam tego do tego stopnia, że ​​po prostu go aliasuję f, ale możesz też użyć bardziej szczegółowego format. na przykład'Hello {0}!'.format(name)

adamJLev
źródło
1
W nowoczesnych przeglądarkach są jeszcze prostsze podejścia: stackoverflow.com/a/41052964/120296
David
3
@david Ciąg szablonów wcale nie jest tym samym. Są cukrem syntaktycznym do łączenia łańcuchów, co jest miłe, ale nie takie samo jak formatowanie. Działają natychmiast, dlatego zamienniki muszą być objęte zakresem w momencie definicji. Z tego powodu nie można przechowywać ich w pliku tekstowym lub bazie danych. W rzeczywistości jedynym sposobem na przechowywanie ich w dowolnym miejscu jest włączenie ich w funkcję. Sformatowana funkcja łańcucha przyjmuje zamiany pozycyjne, które nie dbają o nazwę zmiennej.
krowe2
131

Wiele z powyższych funkcji (oprócz Juliana Jelfa) zawiera następujący błąd:

js> '{0} {0} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 3.14 afoobc foo

Lub w przypadku wariantów, które liczą się wstecz od końca listy argumentów:

js> '{0} {0} {1} {2}'.format(3.14, 'a{0}bc', 'foo');
3.14 3.14 a3.14bc foo

Oto poprawna funkcja. Jest to prototypowa odmiana kodu Juliana Jelfsa, którą ulepszyłem:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; });
};

A oto nieco bardziej zaawansowana wersja tego samego, która pozwala ci uciec od nawiasów klamrowych poprzez ich podwojenie:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
    if (m == "{{") { return "{"; }
    if (m == "}}") { return "}"; }
    return args[n];
  });
};

Działa to poprawnie:

js> '{0} {{0}} {{{0}}} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 {0} {3.14} a{2}bc foo

Oto kolejna dobra implementacja Blair Mitchelmore, z kilkoma fajnymi dodatkowymi funkcjami: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format

GPVOS
źródło
I kolejny, na który nie przyjrzałem się zbyt dokładnie, ale który wydaje się implementować formaty takie jak {0: + $ #, 0,00; - $ #, 0,00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos
Ooh, i taki, który używa formatu interpolacji w języku Python: code.google.com/p/jquery-utils/wiki/…
gpvos
Implementacja formatu, o której wspomniałem dwa komentarze, została przeniesiona do masterdata.se/r/string_format_for_javascript
gpvos
Uwaga: odpowiedź ianj w innym miejscu na tej stronie umożliwia użycie nazwanych zamiast parametrów numerycznych. Jeśli zmienisz jego metodę na wykorzystującą prototypy, będziesz musiał zmienić drugi parametr na funkcję slice.call z 1 na 0.
gpvos
48

Utworzono funkcję formatu, która przyjmuje jako argument kolekcję lub tablicę

Stosowanie:

format("i can speak {language} since i was {age}",{language:'javascript',age:10});

format("i can speak {0} since i was {1}",'javascript',10});

Kod:

var format = function (str, col) {
    col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);

    return str.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
        if (m == "{{") { return "{"; }
        if (m == "}}") { return "}"; }
        return col[n];
    });
};
ianj
źródło
5
Fajny, brakuje tylko: String.prototype.format = function (col) {return format (this, col);}
Erik
10
wolę nie przedłużać łańcucha
ianj
3
w użyciu jest mała literówka: 2. wiersz powinien brzmieć: format („Mogę mówić {0}, ponieważ byłem {1}”, „javascript”, 10);
Guillaume Gendre
Dlaczego nie preferujesz przedłużania łańcucha String.prototype?
Kiquenet
36

Istnieje (nieco) oficjalna opcja: jQuery.validator.format .

Pochodzi z jQuery Validation Plugin 1.6 (przynajmniej).
Całkiem podobny do String.Formatznalezionego w .NET.

Edytuj Naprawiono uszkodzony link.

rsenna
źródło
13

Chociaż nie do końca to, o co prosił Q, zbudowałem taki, który jest podobny, ale używa nazwanych symboli zastępczych zamiast numerowanych. Ja osobiście wolę nazywać argumenty i po prostu wysłać obiekt jako argument (bardziej szczegółowy, ale łatwiejszy do utrzymania).

String.prototype.format = function (args) {
    var newStr = this;
    for (var key in args) {
        newStr = newStr.replace('{' + key + '}', args[key]);
    }
    return newStr;
}

Oto przykładowe użycie ...

alert("Hello {name}".format({ name: 'World' }));
Brian
źródło
8

Korzystając z nowoczesnej przeglądarki obsługującej EcmaScript 2015 (ES6), możesz cieszyć się ciągami szablonów . Zamiast formatowania możesz bezpośrednio wstrzyknąć do niego wartość zmiennej:

var name = "Waleed";
var message = `Hello ${name}!`;

Zauważ, że ciąg szablonu musi być napisany przy pomocy tyknięć (`).

David
źródło
6

Żadna z przedstawionych do tej pory odpowiedzi nie ma oczywistej optymalizacji wykorzystania załączników do jednorazowej inicjalizacji i przechowywania wyrażeń regularnych dla kolejnych zastosowań.

// DBJ.ORG string.format function
// usage:   "{0} means 'zero'".format("nula") 
// returns: "nula means 'zero'"
// place holders must be in a range 0-99.
// if no argument given for the placeholder, 
// no replacement will be done, so
// "oops {99}".format("!")
// returns the input
// same placeholders will be all replaced 
// with the same argument :
// "oops {0}{0}".format("!","?")
// returns "oops !!"
//
if ("function" != typeof "".format) 
// add format() if one does not exist already
  String.prototype.format = (function() {
    var rx1 = /\{(\d|\d\d)\}/g, rx2 = /\d+/ ;
    return function() {
        var args = arguments;
        return this.replace(rx1, function($0) {
            var idx = 1 * $0.match(rx2)[0];
            return args[idx] !== undefined ? args[idx] : (args[idx] === "" ? "" : $0);
        });
    }
}());

alert("{0},{0},{{0}}!".format("{X}"));

Żaden z przykładów nie uwzględnia implementacji formatu (), jeśli już istnieje.


źródło
2
rx2 jest niepotrzebny, zobacz moją implementację; masz (nawiasy) w rx1, ale nie używaj wartości, które przekazują do funkcji wewnętrznej. Myślę też, że jest to oczywista optymalizacja w silniku JavaScript . Czy na pewno nowoczesne przeglądarki nie przeprowadzają już tej optymalizacji za sceną? Perl zrobił to w 1990 roku. Masz rację, że wokół funkcji powinno być opakowanie, aby sprawdzić, czy jest już zaimplementowane.
gpvos,
1
Również test (args [idx] === "") wydaje mi się zbędny: jest już objęty pierwszym testem w tym wierszu, ponieważ jest niezdefiniowany! == "".
gpvos
4

To moje:

String.format = function(tokenised){
        var args = arguments;
        return tokenised.replace(/{[0-9]}/g, function(matched){
            matched = matched.replace(/[{}]/g, "");
            return args[parseInt(matched)+1];             
        });
    }

Nie jest kuloodporny, ale działa, jeśli używasz go rozsądnie.

Julian Jelfs
źródło
4

Minęło późne zakończenie sezonu, ale właśnie patrzyłem na udzielone odpowiedzi i mam wartość mojej dwupłatności:

Stosowanie:

var one = strFormat('"{0}" is not {1}', 'aalert', 'defined');
var two = strFormat('{0} {0} {1} {2}', 3.14, 'a{2}bc', 'foo');

Metoda:

function strFormat() {
    var args = Array.prototype.slice.call(arguments, 1);
    return arguments[0].replace(/\{(\d+)\}/g, function (match, index) {
        return args[index];
    });
}

Wynik:

"aalert" is not defined
3.14 3.14 a{2}bc foo
RickL
źródło
3

Teraz możesz używać literałów szablonów :

var w = "the Word";
var num1 = 2;
var num2 = 3;

var long_multiline_string = `This is very long
multiline templete string. Putting somthing here:
${w}
I can even use expresion interpolation:
Two add three = ${num1 + num2}
or use Tagged template literals
You need to enclose string with the back-tick (\` \`)`;

console.log(long_multiline_string);

Arek Kostrzeba
źródło
3
Byłem podekscytowany literałami szablonów, dopóki nie zobaczyłem, że działają one tylko wtedy, gdy ciąg jest zdefiniowany obok zmiennych zastępujących. Dla mnie to czyni ich prawie bezużytecznymi; z tego czy innego powodu większość moich ciągów jest zdefiniowana oddzielnie od kodu, który je wypełnia / używa.
kamień
2

Oto moja wersja, która jest w stanie uciec z „{” i wyczyścić nieprzypisane miejsca.

function getStringFormatPlaceHolderRegEx(placeHolderIndex) {
    return new RegExp('({)?\\{' + placeHolderIndex + '\\}(?!})', 'gm')
}

function cleanStringFormatResult(txt) {
    if (txt == null) return "";

    return txt.replace(getStringFormatPlaceHolderRegEx("\\d+"), "");
}

String.prototype.format = function () {
    var txt = this.toString();
    for (var i = 0; i < arguments.length; i++) {
        var exp = getStringFormatPlaceHolderRegEx(i);
        txt = txt.replace(exp, (arguments[i] == null ? "" : arguments[i]));
    }
    return cleanStringFormatResult(txt);
}
String.format = function () {
    var s = arguments[0];
    if (s == null) return "";

    for (var i = 0; i < arguments.length - 1; i++) {
        var reg = getStringFormatPlaceHolderRegEx(i);
        s = s.replace(reg, (arguments[i + 1] == null ? "" : arguments[i + 1]));
    }
    return cleanStringFormatResult(s);
}
Feng
źródło
2

Poniższa odpowiedź jest prawdopodobnie najskuteczniejsza, ale ma zastrzeżenie, że nadaje się tylko do mapowania argumentów od 1 do 1. Wykorzystuje to najszybszy sposób konkatenacji łańcuchów (podobny do budowniczego łańcuchów: tablica łańcuchów połączonych). To jest mój własny kod. Prawdopodobnie potrzebuje jednak lepszego separatora.

String.format = function(str, args)
{
    var t = str.split('~');
    var sb = [t[0]];
    for(var i = 0; i < args.length; i++){
        sb.push(args[i]);
        sb.push(t[i+1]);
    }
    return sb.join("");
}

Użyj tego jak:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));
Skychan
źródło
2
Zaakceptowana odpowiedź jest najlepszą odpowiedzią. Daję unikalną odpowiedź, która jest przydatna w scenariuszach, w których chcesz wycisnąć każdą możliwą wydajność (długie pętle) i masz mapowanie argumentów 1: 1. Bardziej wydajne cuz replace () i nowa Regex () wraz z wykonywaniem wyrażenia regularnego zdecydowanie zużywa więcej cykli procesora niż pojedynczy split ().
Skychan
Nie ma potrzeby -1. Nie, nie ma szerokiego zastosowania, ale ma rację, byłoby to nieco bardziej wydajne. Dla autora pytanie architektoniczne: jaki rodzaj aplikacji miałby do czynienia z tak dużym zestawem danych na kliencie, że taka optymalizacja byłaby wymagana?
Michael Blackburn
Dobry punkt Michael Blackburn. Większość sytuacji jest prawdopodobnie w porządku. W moim przypadku miałem do czynienia z dużą ilością danych (warianty produktów w ramach grupy produktów, więc być może setki wariantów) i moim zdaniem jest to, że ponieważ użytkownicy zazwyczaj mają otwartych wiele kart przeglądarki, a każda witryna ma tendencję do zużywania dużej ilości procesora, który Wolałem tę implementację, aby zachować wysoką wydajność.
Skychan
2

Narusza to zasadę SUCHO, ale jest to zwięzłe rozwiązanie:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);
ilyaigpetrov
źródło
0
<html>
<body>
<script type="text/javascript">
   var str="http://xyz.html?ID={0}&TId={1}&STId={2}&RId={3},14,480,3,38";
   document.write(FormatString(str));
   function FormatString(str) {
      var args = str.split(',');
      for (var i = 0; i < args.length; i++) {
         var reg = new RegExp("\\{" + i + "\\}", "");             
         args[0]=args[0].replace(reg, args [i+1]);
      }
      return args[0];
   }
</script>
</body>
</html>
Kishor Dalwadi
źródło
Jest to w porządku dla identyfikatorów URI, ale do ogólnego użytku, mając jeden ciąg zawierający zarówno format, jak i komponenty, jest bardzo kruchy. Co jeśli Twój ciąg zawiera przecinki?
Michael Blackburn,
0

Nie mogłem uzyskać odpowiedzi Josha Stodoli, ale następujące działało dla mnie. Zwróć uwagę na specyfikację prototype. (Testowane na IE, FF, Chrome i Safari.):

String.prototype.format = function() {
    var s = this;
    if(t.length - 1 != args.length){
        alert("String.format(): Incorrect number of arguments");
    }
    for (var i = 0; i < arguments.length; i++) {       
        var reg = new RegExp("\\{" + i + "\\}", "gm");
        s = s.replace(reg, arguments[i]);
    }
    return s;
}

snaprawdę powinno być klonem z thistak aby nie być metoda destrukcyjna, ale nie jest to naprawdę konieczne.

JellicleCat
źródło
To nie jest destrukcyjna metoda. Kiedy s zostanie ponownie przypisane z wartością zwracaną przez s.replace (), pozostaje to niezmienione.
gpvos
0

Rozwijając świetną odpowiedź adamJLev powyżej , oto wersja TypeScript:

// Extending String prototype
interface String {
    format(...params: any[]): string;
}

// Variable number of params, mimicking C# params keyword
// params type is set to any so consumer can pass number
// or string, might be a better way to constraint types to
// string and number only using generic?
String.prototype.format = function (...params: any[]) {
    var s = this,
        i = params.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), params[i]);
    }

    return s;
};
Annie
źródło
0

Mam plunker, który dodaje go do prototypu łańcucha: string.format Jest nie tylko tak krótki jak niektóre inne przykłady, ale o wiele bardziej elastyczny.

Użycie jest podobne do wersji c #:

var str2 = "Meet you on {0}, ask for {1}";
var result2 = str2.format("Friday", "Suzy"); 
//result: Meet you on Friday, ask for Suzy
//NB: also accepts an array

Dodano także obsługę nazw i właściwości obiektów

var str1 = "Meet you on {day}, ask for {Person}";
var result1 = str1.format({day: "Thursday", person: "Frank"}); 
//result: Meet you on Thursday, ask for Frank
ShrapNull
źródło
0

Możesz także zamknąć tablicę z takimi zamiennikami.

var url = '/getElement/_/_/_'.replace(/_/g, (_ => this.ar[this.i++]).bind({ar: ["invoice", "id", 1337],i: 0}))
> '/getElement/invoice/id/1337

lub możesz spróbować bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
test30
źródło