특정한 목적을 가진 내장 Symbol 값들을 의미한다.
ECMAScript6 에서 도입된 Symbol은 고유하고 변경 불가능한 데이터 타입으로
주로 객체 속성의 키에서 사용된다.
Well-Known Symbols은 언어 내에서 내부적으로 사용되는 연산을 나타내는데
이를 이용하면 기본적인 언어 동작을 확장할 수 있다.
Well-known Symbols 이 도입되기 이전에는
특정 메소드가 실행되면 그에 해당하는 변경할 수 없는 알고리즘이 동작해서
개발자의 의도대로 동작됐다.예를 들어 String.prototype.match를 실행하면 @@match 이름의 알고리즘이 실행됐던것이다.
Well-known Symbols이 도입되므로써 바로 알고리즘이 실행되는것이 아니라
Symbols.match가 실행이된다.만약 해당하는 Well-knonw Symbols이 존재하지 않는다면 바로 정적 알고리즘이 실행된다.
도입 이유
Well-knonw Symbols 를 도입한 이유는
개발자가 임의로 디폴트 알고리즘을 수정할 수 있게 자유도를 부여하기 위함이다. (오버라이딩)
객체의 기본 설명을 커스터마이징할 때 사용하는 Well-Known Symbol
toString()으로 인스턴스 타입을 구하면 [object Object] 와 같은 형태를 반환
이것으로 인스턴스 타입을 명확하게 구할 수 없다.const Sport = funcion(){}; const obj = new Sports(); console.log(obj.toString()); // [object Object] Sports.prototype[Symbol.toStringTag] = "농구"; // 인스턴스 타입 지정 console.log(obj.toString()); // [object 농구] let myObject = { [Symbol.toStringTag]: "MyCustomObject" }; console.log(myObject.toString()); // "[object MyCustomObject]"
객체가 배열의 concat() 메서드에 의해 연결 가능한지, 또는 연결되어야 하는지를 결정하는 데 사용되는 자바스크립트의 Well-Known Symbol
주로 대상(concat의 파라미터)에 설정한다.
일반적으로 두 1차원 배열을 concat 하면 배열을 전개하여 연결한다.
[Symbol.isConcatSpreadable]: false
를 설정하면
원본 값 그대로 concat 된다.const one = [10, 20], two = ["A", "B"]; console.log(one.concat(two)); // [10, 20, "A", "B"] two[Symbol.isConcatSpreadable] = false; console.log(one.concat(two)); // [10, 20, ["A", "B"]
대상에
[Symbol.isConcatSpreadable]: true
를 설정하면
원본 값이 concat 되는게 아니라value만 concat
된다.let array = [1, 2, 3]; let obj = { length: 2, 0: "A", 1: "B" }; console.log(array.concat(obj)); // [1, 2, 3, [object Obj] obj[Symbol.isConcatSpreadable] = true; console.log(array.concat(obj)); // [1, 2, 3, 'A', 'B']
생성자 함수가 파생된 객체를 생성해야 할 때 사용되는 Well-Known Symbol
배열과 같은 내장 객체의 메서드들은
새로운 인스턴스를 만들 때 해당 인스턴스의 생성자를 사용한다.이는 배열의
map
,filter
,slice
등과 같은 메서드를 호출할 때
새로운 배열 인스턴스를 반환한다.이때 반환될 인스턴스의 타입을 커스터마이징 할 수 있다.
class MyArray extends Array { static get [Symbol.species]() { // 빌트인 Array 오브젝트의 @@species 를 오버라이드 return Array; // 이 클래스의 메서드를 통해 생성된 인스턴스의 생성자를 Array로 설정 } } let myArray = new MyArray(1, 2, 3); let mapped = myArray.map(x => x * x); console.log(mapped instanceof MyArray); // false, 원래 인스턴스 타입은 생성자의 Constructor인 MyArray여야한다. console.log(mapped instanceof Array); // true
오브젝트를 primitive 타입으로 변환하는 Well-known Symbol
Symbol.toPrimitive 메서드는 하나의 문자열 인자를 가진다.
이 문자열은 변환 유형을 나타내며
number
,string
,default
중 하나입니다.let obj = { value: 42, [Symbol.toPrimitive](hint) { switch (hint) { case 'number': return this.value; case 'string': return `value: ${this.value}`; case 'default': return this.value.toString(); default: throw new Error('Invalid hint'); } } }; consoel.log(20 * obj); 840 (Number 변환) console.log(20 + obj); // 2042 (string 변환), + 시용 시 문자열로 인식 console.log(`${obj}`); // "value: 42" (string 변환) console.log(obj + ''); // "42" (default 변환), String인지 Number인지 판단하기 어려울 때
const point = {bonus: 100};
console.log(point.valueOf());
const book = {
toString() { return 70 },
valueOf() { return 30 }
}
console.log(book * 20); // 600
const point = {bonus: 100};
console.log(point.valueOf());
const book = {
toString() { return 70 },
// valueOf() { return 30 } 가 없다면 toString() 이 호출된다.
}
console.log(book * 20); // 1400
Symbol.iterator를 사용하면 iterator를 반환하는데
이때 iterator 내부에 있는 프로퍼티와 메서드(next() 등)을 오버라이딩하여
커스터마이징할 수 있다.const obj = { [Symbol.iterator](){ return { count: 0, maxCount: this.maxCount, next() { if (this.count < this.maxCount) return {value: this.count++, done: false}; return {value: undefined, done: true}; } } } } obj.maxCount = 3; for (const value of obj){ console.log(value); }; /* 0 1 2 */
const obj = {}; obj[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; console.log([...obj]); // [1,2,3]
const get = function* {
yield 10;
yield 20;
}
const genObj = gen();
console.log(genObj.next()); // { value: 10, done: false }
const obj = genObj[Symbol.iterator]();
console.log(obj.next()); // { value: 20, done: false }
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // *[Symbol.iterator]()는 제너레이터 함수입니다.
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
Symbol.asyncIterator()를 호출하면
AsyncIterator 인스턴스를 생성하여 반환한다.생성한 인스턴스의 next를 호출하면 {value: 값, done:false} 형태로 변환하며
이 값을 Promise.resolve()의 파라미터 값으로 사용한다.for-await-of 로 반복한다.
async funciton* point(){ yield 10; }; const gen = point(); console.log(gen[Symbol.toStringTag]); // AsyncGenerator console.log(gen[Symbol.asyncIterator]); // function [Symbol.asyncIterator]() { [native code] } console.log(gen[Symbol.asyncIterator]().next); // function next() { [native code] }
async funciton* point(){ yield 10; yield 20; }; const gen = point() async function show() { // AsyncIterator.next() 순차 실행 -> {value: 10, done: false} 반환 // Promise.resolve(param)의 파라미터 값으로 사용하여 for-wait-of 으로 보내고 // value 값을 point에 할당 for await (const point of gen){ console.log(point); }; }; show();