JavaScript에서 문자열 프리미티브와 문자열 객체의 차이점은 무엇입니까?


116

MDN 에서 가져옴

문자열 리터럴 (큰 따옴표 또는 작은 따옴표로 표시됨)과 비 생성자 컨텍스트 (즉, 새 키워드를 사용하지 않음)에서 문자열 호출에서 반환 된 문자열은 기본 문자열입니다. 자바 스크립트는 자동으로 프리미티브를 String 객체로 변환하므로 프리미티브 문자열에 String 객체 메소드를 사용할 수 있습니다. 메서드가 기본 문자열에서 호출되거나 속성 조회가 발생하는 컨텍스트에서 JavaScript는 자동으로 문자열 기본 형식을 래핑하고 메서드를 호출하거나 속성 조회를 수행합니다.

그래서 문자열 프리미티브에 대한 (논리적으로) 연산 (메소드 호출)은 문자열 method에 적용 되기 전에 문자열 객체 (추가 작업)로 변환되기 때문에 문자열 객체에 대한 연산보다 느려 야한다고 생각했습니다 .

그러나이 테스트 케이스 에서는 결과가 반대입니다. 코드 블록-1 빠른 것보다 실행 코드 블록 2는 모두 코드 블록 아래에서 언급 :

코드 블록 -1 :

var s = '0123456789';
for (var i = 0; i < s.length; i++) {
  s.charAt(i);
}

코드 블록 -2 :

var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
    s.charAt(i);
}

결과는 브라우저마다 다르지만 코드 블록 -1 은 항상 더 빠릅니다. 누구든지 이것을 설명해 주시겠습니까? 왜 코드 블록 -1코드 블록 -2 보다 빠릅니다 .


6
를 사용하면 객체 래핑 new String의 또 다른 투명 레이어가 도입 됩니다. typeof new String(); //"object"
Paul S.

무엇에 대해 '0123456789'.charAt(i)?
Yuriy Galanter 2013-06-22

@YuriyGalanter, 문제는 아니지만 왜 code block-1더 빠른지 묻습니다 .
The Alpha

2
문자열 객체는 실제 상황에서 볼 수있는 비교적 드물기 때문에 인터프리터가 문자열 리터럴을 최적화하는 것은 놀라운 일이 아닙니다. 요즘에는 코드가 단순히 해석되는 것이 아니라 뒤에서 발생하는 많은 최적화 레이어가 있습니다.
Fabrício Matté 2013-06-22

2
이것은 이상합니다 : 개정판 2
hjpotter92

답변:


149

JavaScript에는 기본 유형과 객체라는 두 가지 주요 유형 범주가 있습니다.

var s = 'test';
var ss = new String('test');

작은 따옴표 / 큰 따옴표 패턴은 기능면에서 동일합니다. 그 외에도 이름을 지정하려는 동작을 자동 복싱이라고합니다. 따라서 실제로 발생하는 것은 래퍼 유형의 메서드가 호출 될 때 기본 형식이 래퍼 유형으로 변환되는 것입니다. 간단하게 말하세요.

var s = 'test';

기본 데이터 유형입니다. 메서드가 없으며 원시 데이터 메모리 참조에 대한 포인터에 지나지 않으며 훨씬 빠른 임의 액세스 속도를 설명합니다.

s.charAt(i)예를 들어 할 때 어떤 일이 발생 합니까?

이후 s의 인스턴스가 아닌 String자바 스크립트 의지 자동 상자 s가, typeof string그 래퍼의 유형 String으로, typeof object또는 더 정확하게 s.valueOf(s).prototype.toString.call = [object String].

자동 박싱 동작 s은 필요에 따라 래퍼 유형으로 앞뒤로 캐스트 되지만 더 간단한 데이터 유형을 다루기 때문에 표준 작업이 매우 빠릅니다. 그러나 자동 복싱과 Object.prototype.valueOf다른 효과가 있습니다.

자동 박싱을 강제하거나 기본 요소를 래퍼 유형으로 캐스팅하려면을 사용할 수 Object.prototype.valueOf있지만 동작은 다릅니다. 다양한 테스트 시나리오를 기반으로 자동 박싱은 변수의 기본 특성을 변경하지 않고 '필수'방법 만 적용합니다. 그것이 당신이 더 나은 속도를 얻는 이유입니다.


33

이것은 구현에 따라 다르지만 한 번 촬영하겠습니다. V8을 예로 들어 보 겠지만 다른 엔진도 비슷한 접근 방식을 사용한다고 가정합니다.

문자열 프리미티브는 v8::String객체 로 구문 분석됩니다 . 따라서 jfriend00에서 언급 한대로 메서드를 직접 호출 할 수 있습니다 .

반면에 String 객체는 v8::StringObject확장 되는 a로 구문 분석되며 Object완전한 객체가되는 것 외에도 v8::String.

이제는 논리적 일뿐입니다.에 대한 호출 은 메서드를 실행하기 전에 new String('').method()this v8::StringObject의 상자 를 풀어야 v8::String하므로 속도가 느립니다.


다른 많은 언어에서 기본 값에는 메서드가 없습니다.

MDN이 설명하는 방식은 프리미티브의 자동 박싱 작동 방식 ( flav 의 답변 에서도 언급 됨 ), 즉 JavaScript의 프리미티브 y 값이 메서드를 호출 하는 방법을 설명하는 가장 간단한 방법 인 것 같습니다 .

그러나 스마트 엔진은 메서드를 호출해야 할 때마다 primitive-y 문자열 을 String 개체 로 변환하지 않습니다 . 이것은 Annotated ES5 사양 에서도 정보 적으로 언급됩니다 . 프리미티브 값의 속성 (및 "메서드"¹) 해결과 관련하여 :

참고 1 단계에서 생성 할 수있는 개체는 위의 방법을 사용하지 않고는 액세스 할 수 없습니다. 구현은 객체의 실제 생성을 피하도록 선택할 수 있습니다. [...]

매우 낮은 수준에서 문자열은 대부분 변경 불가능한 스칼라 값으로 구현됩니다. 래퍼 구조의 예 :

StringObject > String (> ...) > char[]

원시에서 멀어 질수록 도달하는 데 더 오래 걸립니다. 실제로, String프리미티브는 훨씬 더 자주보다 StringObject따라서는 문자열 프리미티브 사이에 앞뒤로 변환하는 대신 클래스 '(해석) 객체에 대응'을하는 메소드를 추가하는 엔진의 놀라운 아닌, S StringStringObjectMDN의 설명에서 알 수 있듯이.


¹ JavaScript에서 "메서드"는 함수 유형의 값으로 확인되는 속성의 명명 규칙 일뿐입니다.


1
천만에요. =]이제 MDN의 설명이 자동 복싱을 이해하는 가장 쉬운 방법 인 것 같아서 또는 ES 사양에 참조가 있는지 궁금합니다. 지금 확인해야 할 사양 전체를 읽으면 참조를 찾으면 답변을 업데이트하십시오.
Fabrício Matté 2013-06-22

V8의 구현에 대한 훌륭한 통찰력. 복싱은 기능을 해결하기 위해 존재하는 것이 아니라는 점을 덧붙일 것입니다. 이 참조를 메서드에 전달하는 것도 있습니다. 이제 V8이 내장 메서드에 대해 이것을 건너 뛰는 지 확실하지 않지만 String.prototype을 말하도록 자신의 확장을 추가하면 호출 될 때마다 문자열 객체의 박스형 버전을 얻게됩니다.
Ben

17

문자열 리터럴의 경우 속성을 할당 할 수 없습니다.

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined

반면 String Object의 경우 속성을 할당 할 수 있습니다.

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world

1
마지막으로 누군가 우리에게 String물건이 필요한 이유에 동기를 부여 합니다. 감사합니다!
Ciprian Tomoiagă 2016

1
왜 누군가가 이것을 할 필요가 있습니까?
Aditya 2019 년

11

문자열 리터럴 :

문자열 리터럴은 변경 불가능합니다. 즉, 일단 생성되면 상태를 변경할 수 없으므로 스레드로부터 안전합니다.

var a = 's';
var b = 's';

a==b 결과는 두 문자열이 동일한 객체를 참조하는 'true'입니다.

문자열 개체 :

여기에서 두 개의 서로 다른 객체가 생성되고 서로 다른 참조가 있습니다.

var a = new String("s");
var b = new String("s");

a==b 결과는 참조가 다르기 때문에 거짓이됩니다.


1
문자열 객체도 변경할 수 없습니까?
Yang Wang

모두, 바보 같은 언어의 @YangWang ab할당하려고 a[0] = 'X' 성공적으로 실행할 수 있지만 작업이 예상되므로
RUX

"var a = 's'; var b = 's'; a == b 결과는 두 문자열이 동일한 객체를 참조하는 'true'가됩니다."라고 썼습니다. 그것은 정확하지 않습니다. a와 b는 동일한 객체를 참조하지 않습니다. 결과는 동일한 값을 갖기 때문에 참입니다. 이러한 값은 다른 메모리 위치에 저장되므로 하나를 변경해도 다른 값은 변경되지 않습니다!
SC1000

9

당신이 사용하는 경우 new, 당신은 명시 적으로이의 인스턴스를 생성 할 것을 주장하고 개체 . 따라서 String 프리미티브를 래핑 new String하는 Object를 생성합니다. 즉, 모든 작업에 추가 작업 레이어가 포함됩니다.

typeof new String(); // "object"
typeof '';           // "string"

유형이 다르기 때문에 JavaScript 인터프리터는 주석에서 언급했듯이 다르게 최적화 할 수도 있습니다 .


5

신고 할 때 :

var s = '0123456789';

문자열 프리미티브를 만듭니다. 이 문자열 프리미티브에는 프리미티브를 첫 번째 클래스 객체로 변환하지 않고도 메서드를 호출 할 수있는 메서드가 있습니다. 따라서 문자열을 객체로 변환해야하므로 속도가 느리다는 가정은 올바르지 않습니다. 개체로 변환 할 필요가 없습니다. 프리미티브 자체는 메소드를 호출 할 수 있습니다.

이를 본격적인 객체로 변환 (새 속성을 추가 할 수 있음)하는 것은 추가 단계이며 문자열 작업을 더 빠르게 만들지 않습니다 (사실 테스트에서 더 느리게 만드는 것으로 나타났습니다).


문자열 프리미티브가 사용자 정의 속성을 포함한 모든 프로토 타입 속성을 상속하는 이유는 String.prototype무엇입니까?
Yuriy Galanter

1
var s = '0123456789';이 값이 어떻게 메소드를 가질 수 있습니까? 혼란 스럽습니다!
The Alpha

2
@SheikhHeera-프리미티브가 언어 구현에 내장되어 있으므로 인터프리터가 특별한 권한을 부여 할 수 있습니다.
jfriend00

1
@SheikhHeera-마지막 댓글 / 질문을 이해하지 못합니다. 문자열 프리미티브 자체는 자신의 속성을 추가하는 것을 지원하지 않습니다. 이를 허용하기 위해 자바 스크립트에는 문자열 프리미티브와 동일한 메서드를 모두 가지고 있지만 모든면에서 객체처럼 취급 할 수있는 완전한 객체 인 String 객체도 있습니다. 이 이중 형태는 약간 지저분 해 보이지만 99 %의 경우는 프리미티브를 사용하고 문자열 객체보다 더 빠르고 메모리 효율성이 높기 때문에 성능 저하로 수행되었다고 생각합니다.
jfriend00

1
@SheikhHeera "converted to string Object"는 MDN이 어떻게 프리미티브가 메소드를 호출 할 수 있는지 설명하기 위해 그것을 표현하는 방법입니다. 문자 그대로 문자열 개체로 변환되지 않습니다.
Fabrício Matté 2013 년

4

이 질문은 오래 전에 해결되었음을 알 수 있습니다. 문자열 리터럴과 문자열 객체 사이에는 또 다른 미묘한 차이가 있습니다. 아무도 그것에 대해 건드리지 않은 것 같기 때문에 완전성을 위해 작성하겠다고 생각했습니다.

기본적으로 둘 사이의 또 다른 차이점은 eval을 사용할 때입니다. eval ( '1 + 1')은 2를 제공하는 반면 eval (new String ( '1 + 1'))은 '1 + 1'을 제공하므로 특정 코드 블록이 'normally'또는 eval과 함께 실행될 수 있다면 이상한 결과를 낳다


입력 해 주셔서 감사합니다 :-)
The Alpha

와, 정말 이상한 행동입니다. 이 동작을 보여주기 위해 댓글에 작은 인라인 데모를 추가해야합니다.
EyuelDK

생각해 보면 이것은 정상입니다. new String("")객체를 반환하고, 문자열을 평가만을 평가 후면, 등 그대로 모든 것을 다른 반환
펠릭스 검은 머리

3

객체의 존재는 ECMAScript / JavaScript 엔진에서 문자열의 실제 동작과 거의 관련이 없습니다. 루트 범위는 단순히이를위한 함수 객체를 포함하기 때문입니다. 따라서 문자열 리터럴의 경우 charAt (int) 함수가 검색되고 실행됩니다.

실제 객체를 사용하면 표준 동작이 시작되기 전에 charAt (int) 메서드가 객체 자체에서 검색되는 레이어를 하나 더 추가합니다 (위와 동일). 이 경우에는 놀랍도록 많은 양의 작업이 수행됩니다.

BTW 기본 요소가 실제로 객체로 변환된다고 생각하지 않지만 스크립트 엔진은이 변수를 문자열 유형으로 표시하기 만하면 제공된 모든 함수를 찾을 수 있으므로 객체를 호출하는 것처럼 보입니다. 이것이 OO 런타임과 다른 원리로 작동하는 스크립트 런타임임을 잊지 마십시오.


3

문자열 프리미티브와 문자열 객체의 가장 큰 차이점은 객체가 연산자에 대해== 다음 규칙을 따라야한다는 것입니다 .

개체를 비교하는 식은 피연산자가 동일한 개체를 참조하는 경우에만 참입니다.

따라서 문자열 프리미티브는 ==값을 비교하는 것이 편리 하지만 다른 불변 객체 유형 (문자열 객체 포함)이 값 유형처럼 동작하도록 만드는 것은 운이 좋지 않습니다.

"hello" == "hello"
-> true
new String("hello") == new String("hello") // beware!
-> false

(다른 사람들은 속성을 추가 할 수 있기 때문에 문자열 객체가 기술적으로 변경 가능하다는 점에 주목했습니다. 그러나 이것이 유용한 이유는 명확하지 않습니다. 문자열 값 자체는 변경할 수 없습니다.)


오랜만에 질문에 가치를 더 해주셔서 감사합니다 :-)
The Alpha

1

코드는 자바 스크립트 엔진에 의해 실행되기 전에 최적화됩니다. 일반적으로 마이크로 벤치 마크는 컴파일러와 인터프리터가 코드의 일부를 재정렬, 수정, 제거 및 수행하여 더 빠르게 실행되도록하기 때문에 오해의 소지가 있습니다. 즉, 작성된 코드는 목표가 무엇인지 알려주지 만 컴파일러 및 / 또는 런타임이 해당 목표를 달성하는 방법을 결정합니다.

블록 1은 주로 다음과 같은 이유로 더 빠릅니다. var s = '0123456789'; var s = new String ( '0123456789')보다 항상 빠릅니다. 객체 생성의 오버 헤드 때문입니다.

인터프리터가 chartAt ()을 인라인 할 수 있기 때문에 루프 부분은 속도 저하를 유발하는 부분이 아닙니다. 루프를 제거하고 테스트를 다시 실행하면 루프가 제거되지 않은 것과 동일한 속도 비율이 표시됩니다. 즉, 이러한 테스트의 경우 실행시 루프 블록은 정확히 동일한 바이트 코드 / 머신 코드를 갖습니다.

이러한 유형의 마이크로 벤치 마크의 경우 바이트 코드 또는 기계어 코드를 살펴보면 더 명확한 그림을 얻을 수 있습니다.


1
답변 해 주셔서 감사합니다.
The Alpha

0

Javascript에서 문자열 과 같은 기본 데이터 유형 은 비 복합 빌딩 블록입니다. 이것은 단지 값일 뿐이라는 것을 의미합니다. let a = "string value"; 기본적으로 toUpperCase, toLowerCase 등과 같은 기본 제공 메서드가 없습니다.

그러나 다음과 같이 쓰려고하면 :

console.log( a.toUpperCase() ); or console.log( a.toLowerCase() );

이것은 오류를 발생시키지 않고 대신 정상적으로 작동합니다.

무슨 일이야? 글쎄, 당신이 문자열의 속성에 액세스하려고 할 때 aJavascript는 wrapper objectnew String(a);알려진 객체로 문자열을 강제 변환 합니다 .

이 프로세스는 Javascript의 함수 생성자 라는 개념에 연결되며 , 여기서 함수는 새 객체를 생성하는 데 사용됩니다.

당신이 입력하면 new String('String value');여기 String이 비어있는 객체에 할당, 인수를 받아 함수 범위 내 빈 객체를 생성 기능 생성자 인 이 경우, 문자열은 우리가 전에 언급 한 기능을 내장 알려진 모든을 제공합니다. 예를 들어 대문자 작업을 수행하는 등 작업이 완료되면 래퍼 개체가 삭제됩니다.

이를 증명하기 위해 이렇게합시다.

let justString = 'Hello From String Value';
justString.addNewProperty = 'Added New Property';
console.log( justString );

여기서 출력은 정의되지 않습니다. 왜 ? 이 경우 Javascript는 래퍼 문자열 개체를 만들고 새 속성 addNewProperty를 설정 하고 래퍼 개체를 즉시 삭제합니다. 이것이 당신이 정의되지 않는 이유입니다. 의사 코드는 다음과 같습니다.

let justString = 'Hello From String Value';
let wrapperObject = new String( justString );
wrapperObject.addNewProperty = 'Added New Property'; //Do operation and discard

0

3 방향으로 문자열을 정의 할 수 있습니다.

  1. var a = "첫 번째 방법";
  2. var b = String ( "두 번째 방법");
  3. var c = new String ( "세 번째 방법");

// 4를 사용하여 만들 수도 있습니다. var d = a + '';

typeof 연산자를 사용하여 만든 문자열 유형 확인

  • typeof a // "문자열"
  • typeof b // "문자열"
  • typeof c // "객체"


a와 b var를 비교할 때 a==b ( // yes)


String 객체를 비교할 때

var StringObj = new String("third way")
var StringObj2 = new String("third way")
StringObj  == StringObj2 // no result will be false, because they have different references
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.