Еквівалент String.format в jQuery


194

Я намагаюся перемістити якийсь код JavaScript з MicrosoftAjax до JQuery. Я використовую еквіваленти JavaScript у MicrosoftAjax популярних методів .net, наприклад, String.format (), String.startsWith () тощо. Чи є в jQuery їх еквіваленти?


Відповіді:


193

Вихідний код ASP.NET AJAX доступний для довідки, так що ви можете вибрати через нього , і включають в себе частини , які ви хочете продовжити використовувати в окремий файл JS. Або ви можете перенести їх до jQuery.

Ось функція форматування ...

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

І ось функції прототипів кінців і запусків із ...

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

2
Не схоже, що в цьому багато чого. Очевидно, що у версії JavaScript немає всіх фантазійних форматів номерів. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna

О, я насправді вже думав про це, але також вважав, що це неможливо через ліцензію, не знав, що вони випустили її під ліцензією Microsoft Permissive License, велике спасибі за це
Waleed Eissa

23
Ліцензія чи ні ліцензія .. Є лише один правильний спосіб написати щось таке просте
adamJLev

1
Конструювання (а потім відкидання) об'єкта RegEx для кожного аргументу кожного разу, коли викликається формат, може перекрити збирач сміття.
mckoss

14
Попередження: Це буде форматуватися рекурсивно: тому, якщо у вас є {0}{1}, {0}спочатку буде замінено, а потім всі випадки {1}в уже заміненому тексті та вихідному форматі будуть замінені.
Zenexer

147

Це більш швидкий / простіший (і прототипічний) варіант функції, який Джош розмістив:

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

Використання:

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

Я використовую це настільки сильно, що я його привласнив просто f, але ви також можете використовувати більш багатослівний format. напр'Hello {0}!'.format(name)


1
З сучасними браузерами існують ще більш прості підходи: stackoverflow.com/a/41052964/120296
david

3
Рядок шаблонів @david насправді НЕ є одне і те ж. Вони є синтаксичним цукром для конкатенації рядків, що приємно, але не те саме, що форматування. Вони запускаються миттєво, тому заміни повинні бути в обсязі в точці визначення. Через це ви не можете зберігати їх у текстовому файлі чи базі даних. Насправді єдиний спосіб зберігати їх у будь-якому місці - це привести їх у функцію. Функція відформатованого рядка займає позиційні заміни, які не переймаються назвою змінної.
krowe2

131

Багато з перерахованих вище функцій (крім Джуліана Джелфса) містять таку помилку:

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

Або для варіантів, які відлічуються від кінця списку аргументів:

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

Ось правильна функція. Це прототипний варіант коду Джуліана Джелфса, який я зробив трохи жорсткіше:

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

А ось трохи більш вдосконалена версія тієї самої, яка дозволяє уникнути дужок шляхом їх подвоєння:

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

Це працює правильно:

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

Ось ще одна хороша реалізація Блера Мітчелмора, з купою приємних додаткових функцій: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format


І ще один, на який я не надто придивився, але який, схоже, реалізує формати на зразок {0: + $ #, 0.00; - $ #, 0.00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos

О, і той, який використовує формат інтерполяції Python: code.google.com/p/jquery-utils/wiki/…
gpvos

Реалізація формату, про яку я згадав два коментарі, перейшла до masterdata.se/r/string_format_for_javascript
gpvos

Примітка: відповідь ianj в іншому місці на цій сторінці дозволяє використовувати замість числових параметрів найменування. Якщо ви зміните його метод на такий, який використовує прототипи, вам доведеться змінити другий параметр функції slice.call з 1 на 0.
gpvos

48

Зроблено функцію форматування, яка бере аргумент як колекцію, так і масив

Використання:

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

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

Код:

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

5
Приємно, все, чого не вистачає: String.prototype.format = function (col) {формат повернення (це, col);}
Ерік

10
я вважаю за краще не продовжувати рядок
ianj

3
у використанні є невеликий помилок: 2-й рядок повинен бути: формат ("я можу говорити {0}, оскільки я був {1}", "javascript", 10);
Гійом Джендр

Чому б не віддати перевагу розширення рядка String.prototype?
Кікенет

36

Є (дещо) офіційний варіант: jQuery.validator.format .

Поставляється з jQuery Validation Plugin 1.6 (принаймні).
Досить схожий на String.Formatзнайдений в .NET.

Редагувати виправлене пошкоджене посилання.



13

Хоча це не саме те, що просив Q, я створив подібний, але використовує іменовані заповнювачі замість нумерованих. Я особисто вважаю за краще називати аргументи і просто надсилати об’єкт як аргумент до нього (більш багатослівний, але простіший в обслуговуванні).

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

Ось приклад використання ...

alert("Hello {name}".format({ name: 'World' }));

8

Використовуючи сучасний браузер, який підтримує EcmaScript 2015 (ES6), ви можете насолоджуватися рядками шаблонів . Замість форматування ви можете безпосередньо ввести в нього значення змінної:

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

Зверніть увагу, що рядок шаблону має бути записана за допомогою зворотних кліщів (`).


6

Жоден із представлених відповідей поки що не має очевидної оптимізації використання корпусу для ініціалізації одного разу та збереження регулярних виразів для наступних звичаїв.

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

Також жоден із прикладів не поважає виконання формату (), якщо такий вже існує.


2
rx2 непотрібний, дивіться мою реалізацію; у вас є (дужки) в rx1, але не використовуйте значення, яке вони передають внутрішній функції. Крім того, я думаю, що це очевидна оптимізація для роботи в Javascript . Ви впевнені, що сучасні браузери вже не проводять цю оптимізацію за кадром? Perl зробив це в 1990 році. Ви праві, що навколо функції має бути обгортка, щоб перевірити, чи вона вже реалізована.
gpvos

1
Також тест на (args [idx] === "") для мене виглядає зайвим: він вже охоплений першим тестом у цьому рядку, тому що невизначений! == "".
gpvos

4

Ось моя:

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

Не є кулезахисним, але працює, якщо ви розумно його використовуєте.


4

Шлях до пізнього сезону, але я щойно дивився на наведені відповіді і мій туппенс вартий:

Використання:

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

Спосіб:

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

Результат:

"aalert" is not defined
3.14 3.14 a{2}bc foo

3

Тепер ви можете використовувати шаблони літератури :

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


3
Я був у захваті від літеральних шаблонів, поки не побачив, що вони працюють лише тоді, коли рядок визначено поряд із змінними змін. Мені це робить їх досить непотрібними; з тієї чи іншої причини більшість моїх рядків визначаються окремо від коду, який заповнює / використовує їх.
камінь

2

Ось моя версія, яка може вийти з режиму "{" та очистити ці непризначені власники місць.

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

2

Наступна відповідь, мабуть, є найбільш ефективною, але має застереження лише придатності для відображення аргументів від 1 до 1. Тут використовується найшвидший спосіб об'єднання рядків (схожий на stringbuilder: масив рядків, з'єднаних). Це мій власний код. Напевно, потрібен кращий роздільник.

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

Використовуйте його так:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));

2
Прийнята відповідь - найкраща відповідь. Я даю унікальну відповідь, яка корисна в сценаріях, коли ви хочете стиснути всі можливі ефективність (довгі петлі), і у вас є 1: 1 відображення аргументів. Більш ефективна заміна cuz () та новий Regex () разом із виконанням регулярного виразів, безумовно, використовує більше циклів процесора, ніж один спліт ().
Скичан

Не потрібно -1. Ні, це не широко застосовується, але він правильний, це було б трохи ефективніше. Тепер, до автора, питання архітектури: який саме додаток матиме справу з таким великим набором даних про клієнта, що потрібна така оптимізація?
Майкл Блекберн

Хороший момент Майкл Блекберн. Більшість ситуацій, ймовірно, добре. У моєму випадку я мав справу з великим набором даних (варіації продуктів у межах групи продуктів, тому, можливо, сотні варіантів), і я вважаю, що оскільки користувачі, як правило, відкривають багато вкладок браузера, і кожен веб-сайт, як правило, висмоктує багато процесора, Я віддав перевагу цій реалізації для підтримки високої ефективності.
Скічан

2

Це порушує принцип DRY, але це стисле рішення:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);

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>

Це добре для URI, але для загального використання, якщо одна рядок містить і формат, і компоненти дуже крихкі. Що робити, якщо ваш рядок містить коми?
Майкл Блекберн

0

Я не міг отримати відповідь Джоша Стодоли про роботу, але наступне працювало для мене. Зверніть увагу на специфікаціюprototype . (Тестовано на IE, FF, Chrome і 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;
}

sна самому справі повинна бути клоном з thisтак, щоб не бути руйнівним методом, але це на самому справі не потрібно.


Це не деструктивний метод. Коли s переназначається зі зворотним значенням s.replace (), це залишається недоторканим.
gpvos

0

Розгорнувшись на чудовій відповіді adamJLev вище , ось версія 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;
};

0

У мене є планкер, який додає його до прототипу рядка: string.format Він не такий короткий, як деякі інші приклади, але набагато гнучкіший.

Використання схоже на версію 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

Крім того, додана підтримка використання імен та властивостей об'єкта

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

0

Ви також можете закрити масив подібними замінами.

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

або ви можете спробувати bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.