Чи використовує javascript незмінні або змінні рядки? Чи потрібен мені "конструктор струн"?
Чи використовує javascript незмінні або змінні рядки? Чи потрібен мені "конструктор струн"?
Відповіді:
Вони незмінні. Ви не можете змінити символ у рядку з чимось подібним var myString = "abbdef"; myString[2] = 'c'
. Методи роботи з рядками , такі як trim
, slice
повертають нові рядки.
Таким же чином, якщо у вас є дві посилання на один і той же рядок, зміна одного не впливає на інше
let a = b = "hello";
a = a + " world";
// b is not affected
Однак я завжди чув те, що згадував Еш у своїй відповіді (що використання Array.join швидше для конкатенації), тому я хотів перевірити різні способи об'єднання рядків і абстрагування найшвидшого шляху в StringBuilder. Я написав кілька тестів, щоб побачити, чи це правда (це не так!).
Це те, що я вважав, що це найшвидший спосіб, хоча я думав, що додавання виклику методу може зробити його повільніше ...
function StringBuilder() {
this._array = [];
this._index = 0;
}
StringBuilder.prototype.append = function (str) {
this._array[this._index] = str;
this._index++;
}
StringBuilder.prototype.toString = function () {
return this._array.join('');
}
Ось тести на швидкість роботи. Усі троє створюють гігантську струну, що складається зі з’єднання "Hello diggity dog"
сто тисяч разів у порожню струну.
Я створив три типи тестів
Array.push
таArray.join
Array.push
, а потім використанняArray.join
Тоді я створив ті самі три тести, абстрагувавши їх StringBuilderConcat
, StringBuilderArrayPush
і StringBuilderArrayIndex
http://jsperf.com/string-concat-without-sringbuilder/5 Будь ласка, зайдіть туди і запустіть тести, щоб ми могли отримати хороший зразок. Зауважте, що я виправив невелику помилку, тому дані для тестів будуть видалені, я оновлю таблицю, коли буде достатньо даних про продуктивність. Перейдіть на сторінку http://jsperf.com/string-concat-without-sringbuilder/5 для старої таблиці даних.
Ось кілька номерів (останнє оновлення в Ma5rch 2018), якщо ви не хочете перейти за посиланням. Кількість на кожному тесті становить 1000 операцій / секунду (чим вище, тим краще )
| Browser | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988 | 1006 | 2902 | 963 | 1008 | 2902 |
| Firefox 65 | 1979 | 1902 | 2197 | 1917 | 1873 | 1953 |
| Edge | 593 | 373 | 952 | 361 | 415 | 444 |
| Exploder 11 | 655 | 532 | 761 | 537 | 567 | 387 |
| Opera 58.0.3135 | 1135 | 1200 | 4357 | 1137 | 1188 | 4294 |
Знахідки
В наші дні всі вічнозелені браузери добре обробляють об'єднання рядків. Array.join
допомагає лише IE 11
Загалом, Opera найшвидша, у 4 рази швидша за Array.join
Firefox займає друге місце і Array.join
лише трохи повільніше у FF, але значно повільніше (в 3 рази) у Chrome.
Хром третій, але строковий конмат у 3 рази швидший, ніж Array.join
Створення StringBuilder, здається, не надто впливає на продуктивність.
Сподіваюся, що хтось інший вважає це корисним
Різний тестовий випадок
Оскільки @RoyTinker подумав, що мій тест був помилковим, я створив новий випадок, який не створює великої рядки, об'єднуючи ту саму рядок, він використовує різний символ для кожної ітерації. З'єднання струн все ще здавалося швидшим або таким же швидким. Давайте проведемо ці тести.
Я пропоную всім думати про інші способи перевірити це, і сміливо додайте нові посилання на різні тестові справи нижче.
join
конкатенації проти рядків, отже, побудови масиву до тесту. Я не думаю, що це обман, якщо ця мета зрозуміла (і join
внутрішньо перераховує масив, тому також не обманювати опускати for
цикл із join
тесту).
Array.join
з книги носорогів :
У JavaScript рядки - це незмінні об'єкти, це означає, що символи всередині них можуть не змінюватися і що будь-які операції над рядками фактично створюють нові рядки. Рядки присвоюються посиланням, а не значенням. Загалом, коли об'єкт призначається за допомогою посилання, зміна, внесена до об'єкта через одну посилання, буде видима через усі інші посилання на об'єкт. Оскільки рядки не можуть бути змінені, однак, ви можете мати кілька посилань на об'єкт рядка і не хвилюватися, що значення рядка зміниться, не знаючи про це
null
undefined
number
та boolean
. Рядки присвоюються за значенням, а не за посиланням і передаються як такі. Таким чином, рядки не просто незмінні, вони є цінністю . Зміна рядка "hello"
бути таким "world"
, як вирішити, що число 3 - це число 4 ... це не має сенсу.
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
об'єкти, створені за допомогою конструктора рядків, є обгортками навколо рядкових значень JavaScript. Ви можете отримати доступ до значення рядка типу в коробці за допомогою .valueOf()
функції - це також стосується Number
об'єктів і значень чисел. Важливо зазначити, що String
об'єкти, створені за допомогою new String
, не є власне рядками, а є обгортками або полями навколо рядків. Див. Es5.github.io/#x15.5.2.1 . Про те, як перетворюють речі на об’єкти, дивіться es5.github.io/#x9.9
Порада щодо продуктивності:
Якщо вам потрібно об'єднати великі рядки, покладіть частини рядка в масив і скористайтеся Array.Join()
методом, щоб отримати загальний рядок. Це може бути в багато разів швидше для об'єднання великої кількості рядків.
У StringBuilder
JavaScript немає .
Просто для уточнення для простих розумів, як моя (від MDN ):
Незмінними є об'єкти, стан яких неможливо змінити, коли об’єкт створений.
Рядок і цифри є незмінними.
Незмінне означає, що:
Можна вказати змінну точку імені на нове значення, але попереднє значення все ще зберігається в пам'яті. Звідси необхідність збору сміття.
var immutableString = "Hello";
// У наведеному вище коді створюється новий об'єкт зі значенням рядка.
immutableString = immutableString + "World";
// Зараз ми додаємо "Світ" до існуючого значення.
Це виглядає так, що ми мутуємо рядок 'незмінний стринг', але це не так. Замість цього:
При додаванні "незмінного строку" зі значенням рядка відбуваються такі події:
- Отримано існуюче значення "незмінногоString"
- "Світ" додається до існуючого значення "незмінногоСтринга"
- Потім отримане значення розподіляється на новий блок пам'яті
- Об'єкт "muttableString" тепер вказує на щойно створений простір пам'яті
- Раніше створений простір пам'яті тепер доступний для збору сміття.
Значення типу рядка незмінне, але об'єкт String, який створюється за допомогою конструктора String (), є змінним, оскільки це об'єкт, і ви можете додати до нього нові властивості.
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
Тим часом, хоча ви можете додати нові властивості, ви не можете змінити вже наявні властивості
Скріншот тесту на консолі Chrome
На закінчення 1. 1. усе значення типу рядка (примітивний тип) є незмінним. 2. Об'єкт String є змінним, але значення типу рядка (примітивний тип), який він містить, є незмінним.
new String
створює змінну обгортку навколо непорушної струни
String
об’єкта (обгортки), тобто не змінюється (за замовчуванням; як і будь-який інший об'єкт, ви можете зателефонувати Object.freeze
на нього, щоб зробити його незмінним). Але тип примітивного рядкового значення, міститься в String
обгортці об'єкта чи ні, завжди незмінний.
Що стосується вашого питання (у вашому коментарі до відповіді Еша) про StringBuilder в ASP.NET Ajax, експерти, здається, не погоджуються з цього питання.
Крістіан Венц у своїй книзі « Програмування ASP.NET AJAX» (O'Reilly) каже, що «такий підхід не має жодного вимірюваного впливу на пам’ять (насправді реалізація здається тик повільнішою, ніж стандартний підхід)».
З іншого боку, Gallo та ін в своїй книзі ASP.NET AJAX в дії (Manning) говорять про те, що "Коли кількість рядків для об'єднання зростає, конструктор струн стає важливим об'єктом, щоб уникнути величезних падінь продуктивності".
Я думаю, вам знадобиться зробити власний бенчмаркінг, і результати можуть також відрізнятися між браузерами. Однак, навіть якщо це не покращує продуктивність, воно все ще може вважатися "корисним" для програмістів, які звикли кодувати StringBuilders на таких мовах, як C # або Java.
Це пізній пост, але серед відповідей я не знайшов гарної цитати книги.
Ось певний виняток, окрім надійної книги:
Рядки незмінні в ECMAScript, тобто, коли вони створюються, їх значення не можуть змінюватися. Щоб змінити рядок, який утримується змінною, початкову рядок потрібно знищити, а змінну заповнити іншою рядком, що містить нове значення ... —Професійний JavaScript для веб-розробників, 3-е видання, стор.43
Тепер відповідь, яка цитує уривок книги Носорога, є правильним про незмінність рядків, але неправильна фраза "Строки призначаються за посиланням, а не за значенням". (ймовірно, вони спочатку мали на меті поставити слова навпаки).
Помилка "посилання / значення" пояснюється в "Професійному JavaScript", главі під назвою "Примітивні та еталонні значення":
П'ять примітивних типів ... [є]: Undefined, Null, Boolean, Number та String. Кажуть, що до цих змінних можна отримати значення, оскільки ви маніпулюєте фактичним значенням, яке зберігається в змінній. —Професійний JavaScript для веб-розробників, 3-е видання, стор.85
що протистоїть об'єктам :
Коли ви маніпулюєте об'єктом, ви дійсно працюєте над посиланням на цей об’єкт, а не над самим власним об'єктом. З цієї причини, як кажуть, доступ до таких значень здійснюється шляхом посилання. —Професійний JavaScript для веб-розробників, 3-е видання, стор.85
Рядки JavaScript справді незмінні.
Рядки в Javascript незмінні