Well-Known Symbols
- ES2019 스펙에서 @@iterator 형태를 볼 수 있음
- @@는
- Well-Known Symbol을 나타내는 기호
- @@match와 Symbol.match가 같음
- 스펙에서는 @@match 형태를 사용하고
- 개발자는 Symbol.match 형태를 사용
- ES2019 기준: 12개 Well-Known Symbol
- Well-Known Symbol이란?
- 스펙에서 알고리즘에 이름을 부여하고
- 이름을 참조하기 위한 빌트인 Symbol 값
- 개발자 코드 우선 실행
- match()를 실행하면 디폴트로 @@match를 실행
- 소스 코드에 Symbol.match를 작성하면
- @@match가 실행되지 않고 Symbol.match가 실행됨
- 개발자 코드로 디폴트 기능을 오버라이딩할 수 있음
- 강좌에서 다루는 것
- isConcatSpreadable, iterator, match
- species, toPrimitive, toStringTag
- 강좌에서 다루지 않는 것
- asyncIterator, hasInstance(instanceof 처리)
- replace, search, split는 match와 비슷
- unscopables는 "use strict"에서 with 문을 사용할 수 없음
Symbol.toStringTag
- Object.prototype.toString()의 확장
- toString()으로 인스턴스 타입을 구하면
- [object Object] 형태로 반환
- 인스턴스 타입을 명확하게 구할 수 없음
const Book = function(){};
const obj = new Book();
log(obj.toString());
log({}.toString());
// [object Object]
// [object Object]
- Symbol.toStringTag로 구분 가능
- [object Object]에서 두 번째에 표시될 문자열을 작성
- 예: "ABC" 지정, [object "ABC"]로 반환
- prototype에 연결하여 작성
prototype에 연결하여 작성
const Sports = function(){};
const obj = new Sports();
log(obj.toString());
Sports.prototype[Symbol.toStringTag] = "농구";
log(obj.toString());
// [object Object]
// [object 농구]
- 첫 번째의 obj.toString()을 실행하면
- 인스턴스 타입을 반환하며
- [object Object]가 반환됨
- function으로 만들었는데 Object가 반환됨
- Sports.prototype[Symbol.toStringTag] = "농구";
- prototype에 Symbol.toStringTag를 연결하고
- [object Object]에서 두 번째의 Object에 표시될 문자를 "농구"로 작성함
- 표시될 문자를 임의로 작성할 수 있음
- function마다 지정할 수 있으므로 자세하게 구분하여 작성할 수 있음
- 두 번째의 obj.toString()을 호출하면
- [object 농구] 출력
- 즉, Symbol.toStringTag에 작성한 문자가 출력됨
Symbol.isConcatSpreadable
- Array.prototype.concat()은
- 배열의 엘리먼트를 전개하여 반환
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] = true
- one 배열 끝에 two 배열의 엘리먼트를 하나씩 연결
- [Symbol.isConcatSpreadable] = false
- 전개하지 않고 two 배열 자체를 연결
- 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
- Symbol.species는 constructor를 반환
- constructor를 실행하면 인스턴스를 생성하여 반환하므로
- 결국, 인스턴스를 반환
- Symbol.species를 오버라이드하면
- 다른 인스턴스를 반환할 수 있다는 의미
- 우선 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 인스턴스를 반환
Symbol.species 기능
class Sports extends Array {};
const obj = new Sports(10, 20, 30);
const one = obj.slice(1, 2);
log(one);
// [20]
- class Sports extends Array{}
- 빌트인 Array 오브젝트를 상속(확장, 연결) 받음
- const obj = new Sports(10, 20, 30);
- 인스턴스를 생성
- const one = obj.slice(1, 2);
- obj 인스턴스의 slice()를 호출하면
- slice() 처리 결과를 인스턴스에 설정하여 인스턴스 반환
- 이렇게 인스턴스의 메소드를 호출했을 때, 인스턴스를 반환하도록 하는 것이 Symbol.species 기능
Symbol.species 오버라이드
- Symbol.species는
- static 악세서 프로퍼티이며
- getter만 있고 setter는 없음
class Sports extends Array {
static get [Symbol.species](){
return Array;
}
};
const obj = new Sports(10, 20);
- Symbol.species를 사용할 수 있는 빌트인 오브젝트
- Array, Map, Set, RegExp
- Promise, ArrayBuffer, TypedArray
- 빌트인 오브젝트를 상속받은 class에
- Symbol.species를 작성하면 빌트인 오브젝트의 @@species가 오버라이드됨
- 인스턴스 바꾸기
인스턴스 바꾸기
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{}
- 빌트인 Array 오브젝트를 상속받음
- static get [Symbol.species](){
return Array;
}- 빌트인 Array 오브젝트의 @@species를 오버라이드
- const one = new Sports(10, 20, 30);
- 인스턴스를 생성
- 파라미터 값이 인스턴스에 설정됨
- one intanceof Sports
- Sports로 one을 만들었으므로 true 출력
- const two = one.slice(1, 2);
- Array 오브젝트를 상속받았으므로 one 인스턴스로 slice()를 호출할 수 있음
- slice() 대상은 인스턴스에 설정된 [10, 20, 30]
- 인스턴스를 반환하며 반환되는 인스턴스에 slice()를 결과를 설정
- Symbol.species()로 오버라이드했으므로
- static get [Symbol.species](){}가 호출됨
- 호출에 사용한 one 인스턴스 형태를 반환하지 않고 Array 인스턴스를 반환
- 이처럼 Symbol.species()로 반환할 인스턴스를 변경할 수 있음
- two instanceof Array
- two 인스턴스에는 Array 인스턴스가 할당되어 있으며
- Array 오브젝트로 만들었으므로 true 출력
- two instanceof Sports
- -Sports가 아니라 Array 오브젝트로 two 인스턴스를 만들었으므로 false 출력
Symbol.toPrimitive
- 오브젝트를 대응하는 Primitive 값으로 변환
- 대응, 기대하는 타입
- number, string, default
- 오브젝트를 문자열에 대응
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
- Symbol.toPrimitive() 사용
Symbol.toPrimitive() 사용
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
- 20을 곱하는 숫자 연산으로 처리
- toPrimitive(hint)의 hint에 엔진이 "number"를 설정
- 30을 반환하여 20 * 30 = 600을 출력
- ${obj} + 100
- hint에 "string"이 설정됨
- obj + 50
- hint에 "default"이 설정됨
- "default" == obj
- == 비교는 hint에 "default"가 설정됨
Symbol.iterator
- @@iterator가 있는 빌트인 오브젝트
- String, Array, Map, Set, TypedArray
- 빌트인 Object에는 @@iterator가 없지만
- 개발자 코드로 작성할 수 있음
- 이 절에서 String, Array, Object를 다루고
- Map, Set은 관련된 곳에서 다룸
- TypedArray는 ES6+ 심화 과정에서 다룸
Array.prototype[@@iterator]
- Array 오브젝트의 [Symbol.iterator]()를 호출하면
- 이터레이터 오브젝트 반환
- next()로 배열 엘리먼트 값을 하나씩 구할 수 있음
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}
Object 이터레이션
- 빌트인 Object에는 Symbol.iterator가 없음
- Symbol.iterator가 반복을 처리하므로
- Object에 Symbol.iterator를 작성하면 반복할 수 있음
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 문을 시작하면
- 먼저 obj에서 [Symbol.iterator] 검색
- 이를 위해 obj에 [Symbol.iterator] 작성
- for(const result of obj)를 처음 실행할 때
- obj의 [Symbol.iterator]()가 호출되며 return{} 문을 수행
- obj.maxCount = 2;로 반복 횟수 정의
Symbol.iterator에 generator 함수 연결
- Object{}에 Symbol.iterator를 작성하고
- generator 함수를 연결하면
- 반복할 때마다 yield 수행
const obj = {};
obj[Symbol.iterator] = function*(){
yield 1;
yield 2;
yield 3;
};
log([...obj]);
// [1, 2, 3]
- 연결 구조
- Symbol.iterator의 __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}
Symbol.match
Well-Known Symbol
- Well-Known Symbol을 지원하는 String 메소드
- match()
- replace()
- search()
- split()
- String.prototype.match()
- 문자열에 패턴을 매치하고
- 매치된 결과를 배열로 반환
const result = "Sports".match(/s/);
log(result);
// [s]
Symbol.match()
- 개발자 코드를 함수 블록에 작성
- String.prototype.match() 대신에 Symbol.match()가 실행됨
const sports = {
base: "ball",
[Symbol.match](value){
return this.base.indexOf(value) < 0 ? "없음" : "있음";
}
}
log("al".atch(sports));
// 있음
- Symbol.match = false
- //를 패턴으로 인식하지 않고 문자열로 인식
try {
"/book/".startsWith(/book/);
} catch {
log("정규 표현식으로 처리");
};
let check = /book/;
check[Symbol.match] = false;
log("/book/".startsWith(check));
// 정규 표현식으로 처리
// true
- 메소드를 오버라이드하는 것이므로
- 메소드의 시맨틱은 유지해야 함
'Javascript > [인프런] 자바스크립트 ES6+ 기본' 카테고리의 다른 글
[자바스크립트 ES6+ 기본] 21. Map 오브젝트 (0) | 2022.02.17 |
---|---|
[자바스크립트 ES6+ 기본] 20. Symbol 함수, 메소드 (0) | 2022.02.17 |
[자바스크립트 ES6+ 기본] 18. Symbol 오브젝트 (0) | 2022.02.17 |
[자바스크립트 ES6+ 기본] 17. Generator 오브젝트 (0) | 2022.02.17 |
[자바스크립트 ES6+ 기본] 16. RegExp 오브젝트 (0) | 2022.02.17 |