사양에 따라 두 가지 기본 유형만 객체 속성 키로 사용할 수 있습니다.
문자열 유형 또는
기호 유형.
그렇지 않고 숫자와 같은 다른 유형을 사용하면 문자열로 자동 변환됩니다. 따라서 obj[1]
은 obj["1"]
과 동일하고 obj[true]
는 obj["true"]
와 동일합니다.
지금까지 우리는 문자열만을 사용해왔습니다.
이제 기호를 살펴보고 기호가 우리에게 무엇을 할 수 있는지 살펴보겠습니다.
"기호"는 고유 식별자를 나타냅니다.
이 유형의 값은 Symbol()
사용하여 생성할 수 있습니다.
id = Symbol();
생성 시 기호에 설명(기호 이름이라고도 함)을 제공할 수 있으며 이는 대부분 디버깅 목적에 유용합니다.
// id는 "id"라는 설명이 포함된 기호입니다. let id = Symbol("id");
기호는 고유함이 보장됩니다. 똑같은 설명으로 여러 개의 기호를 만들어도 값은 다릅니다. 설명은 아무 영향도 주지 않는 레이블일 뿐입니다.
예를 들어, 다음은 동일한 설명을 가진 두 개의 기호입니다. 이들은 동일하지 않습니다.
let id1 = Symbol("id"); let id2 = Symbol("id"); 경고(id1 == id2); // 거짓
Ruby나 일종의 "기호"가 있는 다른 언어에 익숙하다면 오해하지 마십시오. JavaScript 기호는 다릅니다.
따라서 요약하면 기호는 선택적 설명이 포함된 "원시 고유 값"입니다. 우리가 그것들을 어디에 사용할 수 있는지 봅시다.
기호는 문자열로 자동 변환되지 않습니다.
JavaScript의 대부분의 값은 문자열로의 암시적 변환을 지원합니다. 예를 들어 거의 모든 값에 alert
할 수 있으며 작동합니다. 상징은 특별합니다. 자동 변환되지 않습니다.
예를 들어 이 alert
오류가 표시됩니다.
let id = Symbol("id"); 경고(ID); // TypeError: 기호 값을 문자열로 변환할 수 없습니다.
이는 문자열과 기호가 근본적으로 다르며 실수로 서로 변환되어서는 안 되기 때문에 엉망이 되는 것을 방지하는 "언어 보호 장치"입니다.
정말로 기호를 표시하려면 다음과 같이 명시적으로 .toString()
호출해야 합니다.
let id = Symbol("id"); 경고(id.toString()); // Symbol(id), 이제 작동합니다
또는 설명만 표시하려면 symbol.description
속성을 가져옵니다.
let id = Symbol("id"); 경고(id.설명); // ID
기호를 사용하면 코드의 다른 부분이 실수로 액세스하거나 덮어쓸 수 없는 객체의 "숨겨진" 속성을 만들 수 있습니다.
예를 들어, 타사 코드에 속하는 user
개체로 작업하는 경우입니다. 우리는 여기에 식별자를 추가하고 싶습니다.
기호 키를 사용해 보겠습니다.
let user = { // 다른 코드에 속함 이름: "존" }; let id = Symbol("id"); 사용자[ID] = 1; 경고( 사용자[ID] ); // 기호를 키로 사용하여 데이터에 액세스할 수 있습니다.
문자열 "id"
대신 Symbol("id")
사용하면 어떤 이점이 있나요?
user
개체는 다른 코드베이스에 속하므로 여기에 필드를 추가하는 것은 안전하지 않습니다. 다른 코드베이스의 사전 정의된 동작에 영향을 미칠 수 있기 때문입니다. 그러나 실수로 기호에 액세스할 수는 없습니다. 타사 코드는 새로 정의된 기호를 인식하지 못하므로 user
개체에 기호를 추가하는 것이 안전합니다.
또한 다른 스크립트가 자체 목적을 위해 user
내부에 자체 식별자를 갖고 싶어한다고 상상해 보세요.
그런 다음 해당 스크립트는 다음과 같이 자체 Symbol("id")
만들 수 있습니다.
// ... let id = Symbol("id"); user[id] = "그들의 ID 값";
기호는 이름이 같더라도 항상 다르기 때문에 당사와 해당 식별자 사이에는 충돌이 없습니다.
…그러나 동일한 목적으로 기호 대신 문자열 "id"
사용하면 충돌이 발생 합니다 .
사용자 = { 이름: "John" }; // 우리 스크립트는 "id" 속성을 사용합니다. user.id = "우리의 ID 값"; // ...다른 스크립트도 해당 목적을 위해 "id"를 원합니다... user.id = "그들의 ID 값" // 붐! 다른 스크립트로 덮어썼습니다!
객체 리터럴 {...}
에서 기호를 사용하려면 기호 주위에 대괄호가 필요합니다.
이와 같이:
let id = Symbol("id"); 사용자 = { 이름: "존", [id]: 123 // "id"가 아님: 123 };
문자열 "id"가 아니라 변수 id
의 값이 키로 필요하기 때문입니다.
기호 속성은 for..in
루프에 참여하지 않습니다.
예를 들어:
let id = Symbol("id"); 사용자 = { 이름: "존", 나이: 30, [ID]: 123 }; for(사용자에게 키 입력) Alert(key); // 이름, 나이 (기호 없음) // 기호를 통한 직접 액세스가 작동합니다. 경고( "직접: " + 사용자[ID] ); // 직접: 123
Object.keys(user)도 이를 무시합니다. 이는 일반적인 "상징적 속성 숨기기" 원칙의 일부입니다. 다른 스크립트나 라이브러리가 객체를 반복하는 경우 예기치 않게 기호 속성에 액세스하지 않습니다.
이와 대조적으로 Object.sign은 문자열과 기호 속성을 모두 복사합니다.
let id = Symbol("id"); 사용자 = { [ID]: 123 }; 복제 = Object.할당({}, 사용자); 경고(클론[ID] ); // 123
여기에는 역설이 없습니다. 그것은 의도적으로 설계된 것입니다. 객체를 복제하거나 객체를 병합할 때 일반적으로 모든 속성( id
와 같은 기호 포함)이 복사되기를 원한다는 아이디어입니다.
우리가 본 것처럼 이름이 같더라도 일반적으로 모든 기호는 다릅니다. 그러나 때로는 같은 이름의 기호가 동일한 엔터티가 되기를 원할 때도 있습니다. 예를 들어, 우리 애플리케이션의 다른 부분은 정확히 동일한 속성을 의미하는 "id"
기호에 액세스하려고 합니다.
이를 달성하기 위해 전역 기호 레지스트리가 존재합니다. 그 안에 기호를 만들고 나중에 액세스할 수 있으며, 동일한 이름으로 반복적으로 액세스하면 정확히 동일한 기호가 반환된다는 것을 보장합니다.
레지스트리에서 기호를 읽으려면(없는 경우 생성) Symbol.for(key)
사용하십시오.
해당 호출은 전역 레지스트리를 확인하고 key
로 설명된 기호가 있으면 이를 반환하고, 그렇지 않으면 새 기호 Symbol(key)
생성하여 지정된 key
로 레지스트리에 저장합니다.
예를 들어:
// 전역 레지스트리에서 읽습니다. let id = Symbol.for("id"); // 심볼이 존재하지 않으면 생성됩니다. // 다시 읽습니다(아마도 코드의 다른 부분에서) idAgain = Symbol.for("id"); // 같은 기호 경고( id === idAgain ); // 진실
레지스트리 내부의 기호를 전역 기호 라고 합니다. 코드의 어디에서나 액세스할 수 있는 애플리케이션 전체의 기호를 원한다면 그것이 바로 그 목적입니다.
루비 같은데
Ruby와 같은 일부 프로그래밍 언어에서는 이름당 하나의 기호가 있습니다.
보시다시피 JavaScript에서는 전역 기호에 해당됩니다.
전역 기호의 경우 Symbol.for(key)
이름별로 기호를 반환한다는 것을 확인했습니다. 반대의 경우 – 전역 기호로 이름을 반환하려면 다음을 사용할 수 있습니다: Symbol.keyFor(sym)
:
예를 들어:
// 이름으로 심볼 가져오기 let Sym = Symbol.for("이름"); let Sym2 = Symbol.for("id"); // 기호로 이름 얻기 경고(Symbol.keyFor(sym) ); // 이름 경고(Symbol.keyFor(sym2) ); // ID
Symbol.keyFor
내부적으로 전역 기호 레지스트리를 사용하여 기호에 대한 키를 찾습니다. 따라서 비전역 기호에는 작동하지 않습니다. 기호가 전역 기호가 아닌 경우 해당 기호를 찾을 수 없으며 undefined
가 반환됩니다.
즉, 모든 기호에는 description
속성이 있습니다.
예를 들어:
let globalSymbol = Symbol.for("이름"); let localSymbol = Symbol("이름"); 경고(Symbol.keyFor(globalSymbol) ); // 이름, 전역 기호 경고(Symbol.keyFor(localSymbol)); // 정의되지 않았으며 전역이 아님 경고(localSymbol.description); // 이름
JavaScript가 내부적으로 사용하는 많은 "시스템" 기호가 있으며 이를 사용하여 개체의 다양한 측면을 미세 조정할 수 있습니다.
이는 잘 알려진 기호 표의 사양에 나열되어 있습니다.
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.iterator
Symbol.toPrimitive
…등.
예를 들어, Symbol.toPrimitive
사용하면 객체에서 기본 변환으로의 변환을 설명할 수 있습니다. 우리는 곧 그 사용법을 보게 될 것입니다.
다른 기호도 해당 언어 기능을 연구하면 익숙해질 것입니다.
Symbol
고유 식별자의 기본 유형입니다.
기호는 선택적 설명(이름)과 함께 Symbol()
호출을 통해 생성됩니다.
기호는 이름이 같더라도 항상 다른 값입니다. 동일한 이름의 기호를 동일하게 하려면 전역 레지스트리를 사용해야 합니다. Symbol.for(key)
이름이 key
인 전역 기호를 반환(필요한 경우 생성)합니다. 동일한 key
사용하여 Symbol.for
를 여러 번 호출하면 정확히 동일한 기호가 반환됩니다.
기호에는 두 가지 주요 사용 사례가 있습니다.
"숨겨진" 개체 속성.
다른 스크립트나 라이브러리에 "속하는" 객체에 속성을 추가하려면 기호를 만들어 속성 키로 사용할 수 있습니다. 기호 속성은 for..in
에 나타나지 않으므로 실수로 다른 속성과 함께 처리되지 않습니다. 또한 다른 스크립트에는 기호가 없기 때문에 직접 액세스할 수 없습니다. 따라서 해당 속성은 실수로 사용하거나 덮어쓰는 일로부터 보호됩니다.
따라서 우리는 상징적 속성을 사용하여 우리에게 필요하지만 다른 사람들은 보지 말아야 할 것을 "은밀하게" 객체에 숨길 수 있습니다.
JavaScript에서 사용되는 많은 시스템 기호는 Symbol.*
로 액세스할 수 있습니다. 우리는 이를 사용하여 내장된 일부 동작을 변경할 수 있습니다. 예를 들어 튜토리얼의 뒷부분에서는 반복 가능 항목에 Symbol.iterator
사용하고, 객체에서 프리미티브로의 변환 등을 설정하는 데에는 Symbol.toPrimitive
사용할 것입니다.
기술적으로 기호는 100% 숨겨지지 않습니다. 모든 기호를 가져올 수 있는 내장 메서드 Object.getOwnPropertySymbols(obj)가 있습니다. 또한 기호 키를 포함하여 객체의 모든 키를 반환하는 Reflect.ownKeys(obj)라는 메서드가 있습니다. 그러나 대부분의 라이브러리, 내장 함수 및 구문 구조는 이러한 방법을 사용하지 않습니다.