JavaScript 문자열은 변경할 수 없습니까? JavaScript로“문자열 작성기”가 필요합니까?


237

자바 스크립트는 변경 불가능하거나 변경 가능한 문자열을 사용합니까? "문자열 작성기"가 필요합니까?


3
예, y는 변경할 수 없으며 일종의 "문자열 빌더"가 필요합니다. 이 읽기 blog.codeeffects.com/Article/String-Builder-In-Java-Script 또는이 codeproject.com/KB/scripting/stringbuilder.aspx
Kizz

2
흥미롭게도, 이러한 예는 내 대답에서 찾은 결과와 모순됩니다.
Juan Mendes

답변:


294

그들은 불변이다. 문자열 내에서와 같은 문자를 변경할 수 없습니다 var myString = "abbdef"; myString[2] = 'c'. 같은 문자열 조작 방법 trim, slice새로운 문자열을 반환합니다.

같은 방식으로 동일한 문자열에 대한 두 개의 참조가있는 경우 하나를 수정해도 다른 하나에는 영향을 미치지 않습니다

let a = b = "hello";
a = a + " world";
// b is not affected

그러나 Ash의 답변에서 Array.join을 사용하면 연결 속도가 더 빠릅니다. 이것이 사실인지 확인하기 위해 몇 가지 테스트를 작성했습니다 (그렇지 않습니다!).

메서드 호출을 추가하면 속도가 느려질 수 있다고 생각했지만 이것이 가장 빠른 방법이라고 생각했습니다.

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"10 만 번의 빈 줄로 연결된 거대한 줄을 만듭니다 .

세 가지 유형의 테스트를 만들었습니다

  • 사용 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.joinIE 11에만 도움

  • 전반적으로 Opera는 Array.join보다 4 배 빠릅니다.

  • Firefox는 두 번째이며 Array.joinFF에서는 약간 느리지 만 Chrome에서는 상당히 느립니다 (3 배).

  • 크롬은 세 번째이지만 문자열 연결은 Array.join보다 3 배 빠릅니다.

  • StringBuilder를 만드는 것은 성능에 큰 영향을 미치지 않는 것 같습니다.

다른 사람이 유용하다고 생각하기를 바랍니다.

다른 테스트 사례

@RoyTinker는 내 테스트에 결함이 있다고 생각했기 때문에 동일한 문자열을 연결하여 큰 문자열을 만들지 않는 새로운 사례를 만들었습니다. 각 반복마다 다른 문자를 사용합니다. 문자열 연결은 여전히 ​​더 빠르거나 빠릅니다. 테스트를 실행 해 봅시다.

나는 모든 사람들이 이것을 테스트하는 다른 방법을 계속 생각하고 아래의 다른 테스트 사례에 새로운 링크를 자유롭게 추가 할 것을 제안합니다.

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


@ Juan, 당신이 방문하도록 요청한 링크는 112 문자 문자열을 30 번 연결합니다. 균형을 잡는 데 도움이되는 또 다른 테스트가 있습니다-Array.join 대 20,000 개의 서로 다른 1 문자 문자열의 문자열 연결 (IE / FF에서는 훨씬 빠릅니다). jsperf.com/join-vs-str-concat-large-array
Roy Tinker

1
@RoyTinker Roy, Oh Roy, 테스트 설정에서 배열을 생성하기 때문에 테스트가 부정적입니다. 다음은 서로 다른 문자를 사용하는 실제 테스트입니다. jsperf.com/string-concat-without-sringbuilder/7 새로운 테스트 사례를 자유롭게 만들 수 있지만 어레이 생성은 테스트 자체의 일부입니다.
Juan Mendes

@ JuanMendes 내 목표는 테스트 케이스를 좁히고 join문자열 연결을 엄격하게 비교 하여 테스트 전에 배열을 만드는 것이 었습니다. 그 목표가 이해되면 부정 행위라고 생각하지 않습니다 (그리고 join배열을 내부적으로 열거하므로 테스트 for에서 루프 를 생략하는 것도 부정하지 않습니다 join).
Roy Tinker

2
@RoyTinker 그렇습니다. 모든 문자열 빌더는 배열을 구축해야합니다. 문제는 문자열 빌더가 필요한지 여부입니다. 배열에 이미 문자열이있는 경우 여기에서 논의 할 내용에 대한 유효한 테스트 사례가 아닙니다
Juan Mendes

1
@Baltusaj Index / Push가 사용하는 열Array.join
Juan Mendes

41

로부터 코뿔소 책 :

JavaScript에서 문자열은 변경할 수없는 개체이므로 문자열 내의 문자가 변경되지 않고 문자열에 대한 작업이 실제로 새 문자열을 만듭니다. 문자열은 값이 아니라 참조로 지정됩니다. 일반적으로 객체가 참조로 할당되면 하나의 참조를 통해 객체에 대한 변경 사항은 객체에 대한 다른 모든 참조를 통해 볼 수 있습니다. 그러나 문자열을 변경할 수 없으므로 문자열 객체에 대한 여러 참조를 가질 수 있으며 문자열 값을 모르더라도 문자열 값이 변경 될 염려가 없습니다


7
rhino 책의 해당 섹션으로 연결 : books.google.com/…
baudtack 5

116
Rhino의 책 인용문 (따라서이 답변)은 잘못 되었습니다. JavaScript에서 문자열은 기본 값 유형이며 객체 (spec)가 아닙니다 . 사실, ES5의, 그들은 나란히 5 값 유형 중 하나입니다 및 . 문자열에 의해 할당 된 하지 참조 등으로 전달된다. 따라서 문자열은 변경 불가능할뿐만 아니라 value 입니다. 문자열 을 변경 하는 것은 지금부터 숫자 3이 숫자 4임을 결정하는 것과 같습니다. null undefined numberboolean"hello""world"
Benjamin Gruenbaum

8
예, 내 의견처럼 문자열 변경할 수 없지만 참조 유형이나 객체가 아니라 기본 값 유형입니다. 그들은 둘 것없는 볼 수있는 쉬운 방법은 문자열에 속성을 추가 한 다음 그것을 읽으려고하는 것입니다 :var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
벤자민 Gruenbaum

9
@ VidarS.Ramdal 아니요, String문자열 생성자를 사용하여 생성 된 객체는 JavaScript 문자열 값을 둘러싸 는 래퍼 입니다. .valueOf()함수를 사용하여 박스 형식의 문자열 값에 액세스 할 수 있습니다. 이는 Number개체 및 숫자 값 에도 적용 됩니다. 를 String사용하여 생성 된 객체 new String는 실제 문자열이 아니라 문자열 주위의 래퍼 또는 상자 입니다. es5.github.io/#x15.5.2.1을 참조하십시오 . 사물로 변환하는 방법에 대해서는 es5.github.io/#x9.9
Benjamin Gruenbaum

5
어떤 사람들은 문자열이 객체라고 말하는 이유에 관해서는 파이썬이나 Lisp 또는 다른 언어에서 나왔을 것입니다. 스펙에서 "object"라는 단어를 사용하여 모든 종류의 데이텀 (정수)을 의미합니다. ECMA 사양에서 "Object 유형의 멤버"라는 단어를 어떻게 정의하는지 읽어야합니다. 또한, "가치"라는 단어조차 다른 언어의 사양에 따라 다른 것을 의미 할 수 있습니다.
Jisang Yoo

21

성능 팁 :

큰 문자열을 연결해야하는 경우 문자열 부분을 배열에 넣고 Array.Join()메서드를 사용 하여 전체 문자열을 가져옵니다. 이것은 많은 수의 문자열을 연결하는 데 몇 배 더 빠를 수 있습니다.

StringBuilderJavaScript 에는 없습니다 .


나는 stringBuilder가없고, msAjax에 하나 있고, 그 유용성에 대해 숙고하고 있었다
DevelopingChris

5
이것은 불변이 아닌 문자열과 어떤 관련이 있습니까?
baudtack 2016

4
@docgnome : 문자열을 변경할 수 없기 때문에 문자열을 연결하려면 Array.join 방식보다 더 많은 개체를 만들어야합니다.
Juan Mendes

8
위의 Juan의 테스트에 따르면 문자열 연결은 실제로 IE와 Chrome에서 더 빠르지 만 Firefox에서는 느립니다.
Bill Yang

9
답변 업데이트를 고려해보십시오. 오래 전에 사실 이었지만 더 이상은 아닙니다. 참조 jsperf.com/string-concat-without-sringbuilder/5
후안 멘데스에게

8

내 것과 같은 간단한 마음을 분명히하기 위해 ( MDN 출신 ) :

불변은 객체가 생성되면 상태를 변경할 수없는 객체입니다.

문자열과 숫자는 변경할 수 없습니다.

불변은 다음을 의미합니다.

변수 이름이 새 값을 가리 키도록 할 수 있지만 이전 값은 여전히 ​​메모리에 유지됩니다. 따라서 가비지 수집이 필요합니다.

var immutableString = "Hello";

// 위 코드에서 문자열 값을 가진 새 객체가 생성됩니다.

immutableString = immutableString + "World";

// "World"를 기존 값에 추가합니다.

문자열 'immutableString'을 변경하는 것처럼 보이지만 그렇지 않습니다. 대신 :

"immutableString"에 문자열 값을 추가하면 다음과 같은 이벤트가 발생합니다.

  1. "immutableString"의 기존 값이 검색됩니다.
  2. "World"는 기존의 "immutableString"값에 추가됩니다.
  3. 그런 다음 결과 값이 새 메모리 블록에 할당됩니다.
  4. "immutableString"오브젝트가 이제 새로 작성된 메모리 공간을 가리 킵니다.
  5. 이전에 작성된 메모리 공간을 이제 가비지 콜렉션에 사용할 수 있습니다.

4

문자열 유형 값은 변경할 수 없지만 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. 모든 문자열 타입 값 (primitive type)은 불변입니다. 2. String 객체는 변경 가능하지만 포함 된 문자열 유형 값 (기본 유형)은 변경할 수 없습니다.


Javascript String 객체는 불변입니다. developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
prasun

@prasun 그러나 해당 페이지에는 "객체를 제외한 모든 유형은 변경할 수없는 값 (값을 변경할 수 없음)을 정의합니다."문자열 객체는 객체입니다. 그리고 당신이 그것에 새로운 속성을 추가 할 수 있다면 그것은 어떻게 불변인가?
zhanziyang

"문자열 유형"섹션을 읽으십시오. Javascript의 String 링크는 기본 및 Object를 모두 가리키고 나중에 "JavaScript 문자열은 변경할 수 없습니다"라고 표시됩니다. 두 가지 다른 노트에서 충돌하기 때문에이 주제에 대한 문서가 명확하지 않은 것 같습니다
prasun

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

ASP.NET Ajax의 StringBuilder에 대한 귀하의 의견 (Ash의 답변에 대한 귀하의 의견)에 관해서 전문가들은 이에 동의하지 않는 것 같습니다.

Christian Wenz는 자신의 저서 Programming ASP.NET AJAX (O'Reilly)에서 "이 접근 방식은 메모리에 측정 가능한 영향을 미치지 않습니다 (사실 구현은 표준 접근 방식보다 속도가 느립니다)"고 말합니다.

반면 Gallo 등은 자신의 저서 인 ASP.NET AJAX in Action (Manning)에서 "연결할 문자열의 수가 더 많으면 문자열 작성기가 성능 저하를 피하기위한 필수 개체가된다"고 말합니다.

자체 벤치마킹을 수행해야하며 브라우저마다 결과가 다를 수 있습니다. 그러나 성능이 향상되지 않더라도 C # 또는 Java와 같은 언어로 StringBuilder로 코딩하는 데 익숙한 프로그래머에게는 여전히 "유용한"것으로 간주 될 수 있습니다.


1

늦은 게시물이지만 답변 중에서 좋은 책 인용문을 찾지 못했습니다.

신뢰할만한 책을 제외하고는 분명합니다.

ECMAScript에서는 문자열을 변경할 수 없습니다. 즉, 일단 생성 한 후에는 값을 변경할 수 없습니다. 변수가 보유한 문자열을 변경하려면 원래 문자열을 제거하고 변수에 새 값을 포함하는 다른 문자열을 채워야합니다. — Professional Developer for Web Developers, 3rd Ed., p.43

이제 Rhino 서적 발췌문을 인용 한 답은 문자열 불변성에 대해서는 맞지만 "문자열은 값이 아닌 참조로 할당됩니다"라는 잘못된 말입니다. (아마 그들은 원래 단어를 반대 방향으로 놓으려고 했음).

"참조 / 값"오해는 "기본 및 참조 값"이라는 "전문 JavaScript"장에서 설명됩니다.

다섯 가지 기본 유형 ... [정의되지 않음, Null, 부울, 숫자 및 문자열]. 변수에 저장된 실제 값을 조작하기 때문에 이러한 변수는 값으로 액세스한다고합니다. — 웹 개발자를위한 전문 JavaScript, 3rd Ed., p.85

그것은 객체와 반대입니다 .

객체를 조작 할 때 실제 객체 자체가 아니라 해당 객체에 대한 참조 작업을하고 있습니다. 이러한 이유로, 이러한 값은 참조로 액세스한다고합니다. — 웹 개발자를위한 전문 JavaScript, 3rd Ed., p.85


FWIW : Rhino 서적은 아마도 문자열 할당이 문자열 의 내용을 복사하지 않고 포인터를 저장 / 복사 하는 내부 / 구현을 의미합니다 . 그 이후의 텍스트를 기반으로 한 부분은 사고처럼 보이지 않습니다. 그러나 나는 동의한다 : 그들은 "참조로"라는 용어를 오용한다. 구현이 (성능을 위해) 포인터를 전달하기 때문에 "참조로"는 아닙니다. 위키-평가 전략 은이 주제에 대한 흥미로운 내용입니다.
ToolmakerSteve


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.