ES2019 스펙에서 @@iterator 형태를 볼 수 있음
@@는
ES2019 기준: 12개 Well-Known Symbol
Well-Known Symbol이란?
개발자 코드 우선 실행
개발자 코드로 디폴트 기능을 오버라이딩할 수 있음
강좌에서 다루는 것
강좌에서 다루지 않는 것
Object.prototype.toString()의 확장
toString()으로 인스턴스 타입을 구하면
const Book = function(){};
const obj = new Book();
log(obj.toString());
log({}.toString());
// [object Object]
// [object Object]
const Sports = function(){};
const obj = new Sports();
log(obj.toString());
Sports.prototype[Symbol.toStringTag] = "농구";
log(obj.toString());
// [object Object]
// [object 농구]
첫 번째의 obj.toString()을 실행하면
Sports.prototype[Symbol.toStringTag] = "농구";
두 번째의 obj.toString()을 호출하면
const one = [10, 20], two = ["A", "B"];
const show = () => {
log(one.concat(two));
};
show();
two[Symbol.isConcatSpreadable] = true;
show();
two[Symbol.isConcatSpreadable] = false;
show();
// [10, 20, A, B]
// [10, 20, A, B]
// [10, 20, [A, B]]
[Symbol.isConcatSpreadable] = false
Array-like 전개
const one = [10, 20];
const like = {0: "A", 1: "B", length: 2};
const show = () => {
log(one.concat(like));
};
show();
like[Symbol.isConcatSpreadable] = true;
show();
like[Symbol.isConcatSpreadable] = false;
show();
// [10, 20, {0: A, 1: B, length: 2}]
// [10, 20, A, B]
// [10, 20, {0: A, 1: B, length: 2}]
Symbol.species는 constructor를 반환
Symbol.species를 오버라이드하면
우선 Symbol.species와 관련된 개념 살펴보기
const obj = [1, 2, 3];
/*
1. [1, 2, 3]으로 Array 오브젝트를 생성하여 obj에 할당
2. 오른쪽의 obj를 펼쳐서 obj 구조를 보면 prototype은 없고 __proto__만 있으므로
3. obj는 빌트인 Array 오브젝트가 아니라 Array.prototype에 연결된 메소드로 생성한 인스턴스
4. 다만, new 연산자를 사용하지 않았으므로 강좌에서 인스턴스라고 하지 않고 오브젝트라고 한 것
*/
const one = obj.slice(1, 3);
/*
1. 위 코드를 실행한 후의 one과 obj 구조는 차이가 없으며 값 [2, 3]만 다름
2. 이것은 인스턴스에 있는 메소드를 호출하면 메소드 실행 결과값을 반환하지 않고
3. 결과값이 설정된 인스턴스를 반환하기 때문
*/
const two = one.slice(1, 2);
/*
1. 바로 앞에서 반환된 one으로 메소드를 호출할 수 있는 것은 one이 인스턴스이기 때문
2. 또한 slice(1, 2)를 실행하면 결과 값이 설정된 인스턴스를 반환
*/
Array 인스턴스의 메소드를 호출하면 값을 반환하는 것이 아니라
반환할 Array 인스턴스를 생성하고, 메소드에서 구한 값을 반환할 Array 인스턴스에 설정하여, Array 인스턴스를 반환
class Sports extends Array {};
const obj = new Sports(10, 20, 30);
const one = obj.slice(1, 2);
log(one);
// [20]
class Sports extends Array{}
const obj = new Sports(10, 20, 30);
const one = obj.slice(1, 2);
이렇게 인스턴스의 메소드를 호출했을 때, 인스턴스를 반환하도록 하는 것이 Symbol.species 기능
class Sports extends Array {
static get [Symbol.species](){
return Array;
}
};
const obj = new Sports(10, 20);
Symbol.species를 사용할 수 있는 빌트인 오브젝트
빌트인 오브젝트를 상속받은 class에
인스턴스 바꾸기
class Sports extends Array {
static get [Symbol.species](){
return Array;
}
};
const one = new Sports(10, 20, 30);
log(one instanceof Sports);
const two = one.slice(1, 2);
log(two instanceof Array);
log(two instanceof Sports);
// true
// true
// false
class Sports extends Array{}
static get [Symbol.species]()
{
return Array;
}
const one = new Sports(10, 20, 30);
one intanceof Sports
const two = one.slice(1, 2);
Symbol.species()로 오버라이드했으므로
[Symbol.species]()
{}가 호출됨two instanceof Array
two instanceof Sports
-Sports가 아니라 Array 오브젝트로 two 인스턴스를 만들었으므로 false 출력
오브젝트를 대응하는 Primitive 값으로 변환
대응, 기대하는 타입
오브젝트를 문자열에 대응
const point = {bonus: 100};
log(point.toString());
const book = {
toString() {
return "책"
}
};
log(`${book}`);
// [object Object]
// 책
const point = {bonus: 100};
log(point.valueOf());
const book = {
toString() { return 70 },
valueOf() { return 30 }
};
log(book + 20);
// {bonus: 100}
// 600
const obj = {
[Symbol.toPrimitive](hint){
return hint === "number" ? 30 : hint === "string" ? "책" : "default";
}
};
log(20 * obj);
log(`${obj}` + 100);
log(obj + 50);
log("default" == obj);
// 600
// 책100
// default500
// true
20 * obj
${obj}
+ 100
obj + 50
"default" == obj
@@iterator가 있는 빌트인 오브젝트
빌트인 Object에는 @@iterator가 없지만
이 절에서 String, Array, Object를 다루고
[Symbol.iterator]()
를 호출하면 const list = [10, 20];
const obj = list[Symbol.iterator]();
log(obj.next());
log(obj.next());
log(obj.next());
// {value: 10, done: false}
// {value: 20, done: false}
// {value: undefined, done: true}
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 = 2;
for (const value of obj){
log(value);
};
// 0
// 1
엔진이 for-of 문을 시작하면
for(const result of obj)를 처음 실행할 때
[Symbol.iterator]()
가 호출되며 return{} 문을 수행const obj = {};
obj[Symbol.iterator] = function*(){
yield 1;
yield 2;
yield 3;
};
log([...obj]);
// [1, 2, 3]
연결 구조
__proto__
에 제너레이터 오브젝트가 있는 구조제너레이터 오브젝트에
const get = function*(){
yield 10;
yield 20;
};
const genObj = gen();
log(genObj.next());
const obj = genObj[Symbol.iterator]();
log(obj.next());
// {value: 10, done: false}
// {value: 20, done: false}
Well-Known Symbol을 지원하는 String 메소드
String.prototype.match()
const result = "Sports".match(/s/);
log(result);
// [s]
const sports = {
base: "ball",
[Symbol.match](value){
return this.base.indexOf(value) < 0 ? "없음" : "있음";
}
}
log("al".atch(sports));
// 있음
try {
"/book/".startsWith(/book/);
} catch {
log("정규 표현식으로 처리");
};
let check = /book/;
check[Symbol.match] = false;
log("/book/".startsWith(check));
// 정규 표현식으로 처리
// true