Являются ли строки JavaScript неизменяемыми? Нужен ли мне «строитель строк» ​​в JavaScript?


237

Использует ли javascript неизменяемые или изменяемые строки? Нужен ли мне «строитель строк»?


3
Да, они неизменны, и вам нужен какой-то «строитель строк». Прочитайте этот blog.codeeffects.com/Article/String-Builder-In-Java-Script или этот codeproject.com/KB/scripting/stringbuilder.aspx
Кизз

2
Интересно, что эти примеры противоречат моим выводам в моем ответе.
Хуан Мендес

Ответы:


294

Они неизменны. Вы не можете изменить символ в строке с чем-то вроде 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.

  • Chrome - третий, но конкат строки в 3 раза быстрее, чем Array.join

  • Создание StringBuilder, похоже, не сильно влияет на производительность.

Надеюсь, кто-то еще найдет это полезным

Другой тестовый кейс

Поскольку @RoyTinker считал, что мой тест некорректен, я создал новый случай, в котором не создается большая строка путем объединения одной и той же строки, для каждой итерации он использует разные символы. Конкатенация строк все еще казалась более быстрой или такой же быстрой. Давайте запустим эти тесты.

Я предлагаю всем подумать о других способах проверки этого, и не стесняйтесь добавлять новые ссылки в различные тестовые примеры ниже.

http://jsperf.com/string-concat-without-sringbuilder/7


@Juan, ссылка, которую вы просили нас посетить, объединяет строку из 112 символов 30 раз. Вот еще один тест, который может помочь сбалансировать вещи - Array.join против конкатенации строк на 20000 различных строк с 1 символом (объединение намного быстрее в IE / FF). jsperf.com/join-vs-str-concat-large-array
Рой Тинкер,

1
@ RoyTinker Рой, о Рой, ваши тесты обманывают, потому что вы создаете массив в настройках теста. Вот настоящий тест с использованием разных символов jsperf.com/string-concat-without-sringbuilder/7 Не стесняйтесь создавать новые тестовые случаи, но создание массива является частью самого теста
Хуан Мендес,

@JuanMendes Моя цель состояла в том, чтобы сузить тестовый пример строгого сравнения joinпротив конкатенации, следовательно , построение массива до испытания. Я не думаю, что это обман, если эта цель понятна (и joinперечисляет массив внутренне, так что тоже не обмануть пропустить forцикл из joinтеста).
Рой Тинкер

2
@RoyTinker Да, любой сборщик строк потребует сборки массива. Вопрос в том, нужен ли конструктор строк. Если у вас уже есть строки в массиве, то это недопустимый тестовый пример для того, что мы обсуждаем здесь
Хуан Мендес,

1
@Baltusaj Используются столбцы с надписью Index / PushArray.join
Хуан Мендес,

41

из книги носорогов :

В JavaScript строки являются неизменяемыми объектами, что означает, что символы внутри них не могут быть изменены и что любые операции над строками фактически создают новые строки. Строки присваиваются по ссылке, а не по значению. Как правило, когда объект назначается по ссылке, изменение, внесенное в объект посредством одной ссылки, будет видно через все другие ссылки на объект. Однако, поскольку строки нельзя изменить, вы можете иметь несколько ссылок на строковый объект и не беспокоиться о том, что значение строки изменится без вашего ведома.


7
Ссылка на соответствующий раздел книги о носорогах: books.google.com/…
бодтак

116
Книга цитата Rhino (и , таким образом , этот ответ) является неправильной здесь. В JavaScript строки являются примитивными типами значений, а не объектами (spec) . В самом деле, как в ES5, они один из немногих 5 типов значений наряду null undefined numberи boolean. Строки присваиваются по значению, а не по ссылке и передаются как таковые. Таким образом, строки не просто неизменны, они являются ценностью . Изменение строки "hello"на "world"это похоже на решение, что теперь число 3 - это число 4 ... это не имеет смысла.
Бенджамин Грюнбаум

8
Да, как мой комментарий говорит строка является неизменной, но они не являются ссылочными типами , ни они являются объектами - они примитивные типов значений. Простой способ увидеть, что они ни один, - попытаться добавить свойство к строке и затем прочитать ее:var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
Бенджамин Грюнбаум

9
@ VidarS.Ramdal Нет, Stringобъекты, созданные с помощью строкового конструктора, являются оболочками вокруг строковых значений JavaScript. Вы можете получить доступ к строковому значению типа в штучной упаковке, используя .valueOf()функцию - это также верно для Numberобъектов и числовых значений. Важно отметить, что Stringобъекты, созданные с использованием new Stringне реальных строк, а оберток или блоков вокруг строк. См. Es5.github.io/#x15.5.2.1 . О том, как вещи преобразуются в объекты, см. Es5.github.io/#x9.9
Бенджамин Грюнбаум

5
Что касается того, почему некоторые люди говорят, что строки являются объектами, они, вероятно, происходят из Python или Lisp или любого другого языка, где в его спецификации слово «объект» означает любое значение (даже целые числа). Им просто нужно прочитать, как спецификация ECMA определяет слово: «член типа Object». Кроме того, даже слово «значение» может означать разные вещи в соответствии со спецификациями разных языков.
Jisang Yoo

21

Совет по производительности:

Если вам нужно объединить большие строки, поместите части строк в массив и используйте Array.Join()метод, чтобы получить общую строку. Это может быть во много раз быстрее для объединения большого количества строк.

Там нет StringBuilderв JavaScript.


Я знаю, что нет stringBuilder, у msAjax есть такой, и я просто размышлял, полезно ли это или нет
DevelopingChris

5
Какое это имеет отношение к неизменяемости строк или нет?
Бодэкт

4
@docgnome: поскольку строки являются неизменяемыми, для конкатенации строк требуется создать больше объектов, чем в подходе Array.join
Хуан Мендес,

8
Согласно тесту Хуана выше, конкатенация строк на самом деле быстрее в IE и Chrome, а в Firefox медленнее.
Билл Ян

9
Вы можете обновить свой ответ, возможно, это было правдой давно, но это не так. См. Jsperf.com/string-concat-without-sringbuilder/5
Хуан Мендес

8

Просто чтобы уточнить для простых умов, как мой (из MDN ):

Неизменяемыми являются объекты, состояние которых нельзя изменить после создания объекта.

Строка и числа неизменны.

Неизменный означает, что:

Вы можете сделать так, чтобы имя переменной указывало на новое значение, но предыдущее значение все еще сохраняется в памяти. Отсюда и необходимость сбора мусора.

var immutableString = "Hello";

// В приведенном выше коде создается новый объект со строковым значением.

immutableString = immutableString + "World";

// Теперь мы добавляем «Мир» к существующему значению.

Похоже, мы изменяем строку «immutableString», но это не так. Вместо:

При добавлении «immutableString» со строковым значением происходят следующие события:

  1. Существующее значение «immutableString» извлекается
  2. «Мир» добавляется к существующему значению «immutableString»
  3. Результирующее значение затем выделяется для нового блока памяти
  4. Объект «immutableString» теперь указывает на вновь созданное пространство памяти
  5. Ранее созданное пространство памяти теперь доступно для сборки мусора.

4

Значение типа string является неизменным, но объект 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. все значения типа строки (тип примитива) являются неизменяемыми. 2. Объект String является изменяемым, но значение строкового типа (тип примитива), которое он содержит, является неизменным.


Объекты Javascript String являются неизменными developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
prasun,

@prasun, но на этой странице написано: «Все типы, кроме объектов, определяют неизменяемые значения (значения, которые невозможно изменить).» Строковые объекты являются объектами. И как это будет неизменным, если вы можете добавить новые свойства на него?
zhanziyang

прочитайте раздел «Тип строки». Ссылка String в Javascript указывает как на примитив, так и на Object, а затем говорит: «Строки JavaScript являются неизменяемыми». Похоже, что документ не ясен по этой теме, поскольку он конфликтует в двух разных заметках
прасун

6
new Stringгенерирует изменчивую обертку вокруг неизменной строки
tomdemuyt

1
Это очень легко проверить, запустив код @ zhanziyang выше. Вы можете полностью добавить новые свойства к Stringобъекту (оболочке), то есть он не является неизменным (по умолчанию; как и любой другой объект, вы можете вызвать Object.freezeего, чтобы сделать его неизменным). Но примитивный тип строкового значения, независимо от того, содержится он в Stringобъектной обертке или нет, всегда неизменен.
Марк Рид

3

Строки неизменны - они не могут изменяться, мы можем только создавать новые строки.

Пример:

var str= "Immutable value"; // it is immutable

var other= statement.slice(2, 10); // new string

1

Что касается вашего вопроса (в вашем комментарии к ответу Эша) о StringBuilder в ASP.NET Ajax, то эксперты, похоже, с этим не согласны.

Кристиан Венц говорит в своей книге « Программирование ASP.NET AJAX (O'Reilly)», что «этот подход не оказывает какого-либо ощутимого влияния на память (на самом деле, реализация кажется медленнее, чем стандартный подход)».

С другой стороны, Галло и др. В своей книге ASP.NET AJAX в действии (Мэннинг) говорят, что «Когда число объединяемых строк больше, построитель строк становится важным объектом, позволяющим избежать огромных падений производительности».

Я полагаю, вам нужно будет сделать свой собственный сравнительный анализ, и результаты могут отличаться в разных браузерах. Однако, даже если это не улучшает производительность, его все равно можно считать «полезным» для программистов, которые привыкли кодировать с помощью StringBuilders на таких языках, как C # или Java.


1

Это поздний пост, но я не нашел хорошую цитату из ответов.

Вот определенный, кроме как из надежной книги:

Строки являются неизменяемыми в ECMAScript, что означает, что после их создания их значения не могут измениться. Чтобы изменить строку, содержащуюся в переменной, исходная строка должна быть уничтожена, а переменная должна быть заполнена другой строкой, содержащей новое значение ... - Профессиональный JavaScript для веб-разработчиков, 3-е изд., С.43

Теперь, ответ, который цитирует отрывок из книги Носорога, верен в отношении неизменности строк, но неверен: «Строки назначаются по ссылке, а не по значению». (вероятно, они изначально хотели поставить слова противоположным образом).

Неправильное представление «ссылка / значение» разъясняется в разделе «Профессиональный JavaScript», глава «Примитивные и ссылочные значения»:

Пять примитивных типов ... [являются]: Undefined, Null, Boolean, Number и String. Говорят, что эти переменные доступны по значению, потому что вы манипулируете фактическим значением, хранящимся в переменной. —Профессиональный JavaScript для веб-разработчиков, 3-е изд., С.85

это в отличие от объектов :

Когда вы манипулируете объектом, вы действительно работаете со ссылкой на этот объект, а не с самим объектом. По этой причине такие значения считаются доступными по ссылке. —Профессиональный JavaScript для веб-разработчиков, 3-е изд., С.85


FWIW: книга Rhino, вероятно, означает, что внутренне / реализация строкового присваивания хранит / копирует указатель (а не копирует содержимое строки). Это не похоже на случайность с их стороны, основываясь на тексте после этого. Но я согласен: они неправильно используют термин «по ссылке». Это не «по ссылке» только потому, что реализация передает указатели (для производительности). Вики - стратегия оценки - интересное чтение на эту тему.
ToolmakerSteve


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.