##一.Symbol 이란 무엇인가
typeof Symbol() === 'symbol', symbol 은 js 의 7 번째 기본 타입입니다 (원래 있던 6 가지는 null, undefined, Number, Boolean, Object, String). 문자열도 객체도 아닙니다
역할: symbol 은명명 충돌을 회피하는 데 사용되며, 네이티브 객체 개조 (속성 추가) 의 후유증을 해결하고, 속성명이 향후 네이티브 속성명이나 다른 라이브러리 조작과 충돌할 것을 걱정할 필요가 없습니다
##二.구문
Symbol 을 얻는 방법은 3 가지가 있습니다. 다음과 같습니다:
###1.Symbol(desc)
symbol 을 반환합니다. desc 는 옵션이며, symbol.toString() 은`Symbol(${desc})`를 반환합니다. 예를 들어:
var obj = {
a: 1
};
// 不用 new,Symbol 不是构造器
var safeKey = Symbol();
obj[safeKey] = 'value';
console.log(obj[safeKey]); // value
var anotherSafeKey = Symbol('isAnimActive');
console.log(anotherSafeKey); // Symbol(isAnimActive)
위의 var safeKey = Symbol(); 는 조금 이상해 보입니다. new 연산자는 어디에 있을까요? Symbol 은 생성자가 아닙니다. new 연산자로 호출할 수 없습니다. 억지로 new 하면 다음과 같은 오류가 발생합니다:
Uncaught TypeError: Symbol is not a constructor
이것은 확실히 이상합니다. 코딩 규약에서는 일반적으로 첫 글자가 대문자인 것은 타입명을 나타내며, new 연산자로 호출해야 하지만, API 가 제공하는 것은 코딩 규약에 맞지 않는 Symbol 함수입니다
특징:
-
Symbol('key') !== Symbol('key'), Symbol() 을 호출할 때마다 다른 값이 반환됩니다 -
Symbol 은 속성명으로 사용할 수 있으며, 게다가 어떤 것과도 (문자열, 숫자, 다른 Symbol) 같지 않습니다. 따라서 Symbol 형의 속성명은 기존 어떤 속성명과도 충돌하지 않으며, 향후 어떤 새로운 속성명과도 충돌하지 않습니다
-
for...in,Object.keys(obj),Object.getOwnPropertyNames(obj)는 symbol 속성을 스킵합니다 -
Object.getOwnPropertySymbols(obj)는 객체의 모든 symbol 속성명을 반환합니다 -
Reflect.ownKeys(obj)는 객체의 모든 속성명 (symbol 속성명과 문자열 속성명 포함) 을 반환합니다 -
symbol 은 읽기 전용이며, 문자열과 유사합니다. 엄격 모드에서 symbol 에 속성을 추가하면 TypeError 가 오류 발생합니다
-
symbol은 자동으로 문자열로 변환되지 않습니다. symbol 을拼接하려고 하면 TypeError 가 오류 발생합니다. 수동으로 toString() 을 호출한 후拼接할 수 있습니다
샘플 (이전 샘플에 이음) 은 다음과 같습니다:
obj[Symbol('ready')] = true;
//!!! undefined
// 因为特点 1
console.log(obj[Symbol('ready')]); // undefined
console.log(obj); // Object {Symbol(): "value", Symbol(ready): true}
// 跳过 symbol 属性
for (var key in obj) {
console.log(`obj[${key}] = ${obj[key]}`);
} // obj[a] = 1
// 获取所有 obj 上所有 Symbol 类型的属性名
console.log(Object.getOwnPropertySymbols(obj)); // Array [ Symbol(), Symbol(ready) ]
// 获取 objs 上的所有属性名
console.log(Reflect.ownKeys(obj)); // Array [ "a", Symbol(), Symbol(ready) ]
// 只读
var s = Symbol();
s.a = 1;
console.log(s.a); // undefined
// 不会自动转字符串
// console.log(Symbol('123') + '4'); // TypeError: can't convert symbol to string
console.log(Symbol('123').toString() + '4'); // Symbol(123)4
###2.Symbol.for(str)
symbol 레지스트리를 나타내며, 공유 symbol 을 생성하는 데 사용됩니다. 특징은 다음과 같습니다:
-
Symbol.for('ready') === Symbol.for('ready') -
Symbol('str') !== Symbol.for('str')
샘플 (이전의 모든 샘플에 이음):
// Symbol.for()
obj[Symbol.for('ready')] = 'ready';
console.log(obj[Symbol.for('ready')]); // ready
console.log(Object.getOwnPropertySymbols(obj));
// log print: Array [ Symbol(), Symbol(ready), Symbol(ready) ]
P.S. 마지막으로 출력된 배열에는 2 개의 Symbol(ready) 이 있지만, 그것들에 해당하는 symbol 은 같지 않습니다. toString 이 반환하는 결과가 같을 뿐입니다 (특징 2)
주의: 공유 Symbol 의 경우, 속성명이 향후 DOM API 와 충돌하지 않는 것만 보증할 수 있습니다. 다른 코드와 충돌하지 않는 것은 보증할 수 없습니다. 라이브러리 작성자는 그것을 사용해서는 안 됩니다. 비즈니스 코드에서 모듈 간 또는 페이지 간 Symbol 공유가 필요할 때만 사용하는 것을 권장합니다
###3.Symbol.xxx
네이티브 Symbol 을 얻는 데 사용됩니다. 예를 들어 Symbol.iterator. 특징은 다음과 같습니다:
-
새로운 API 는 하위 호환성이 있습니다. 예를 들어 iterable 인터페이스를 구현:
obj[Symbol.iterator] = gen. 오래된 코드에 영향을 주지 않습니다 -
hook 을 준비했습니다. symbol 을借助하여, 향후 새로운 특성 API 는 모두 오래된 코드에 영향을 주지 않습니다
아직 구현되지 않은 새로운 특성:
-
Symbol.hasInstance는instanceof를 확장 -
Symbol.unscopables는 메서드가 동적 스코프에 추가되는 것을 방지 -
Symbol.match는 str.match 를 확장
이러한 Symbol 은 이미 네이티브 Symbol 로 예약되어 있으며, 곧 구현될 것입니다. 향후 (기존 API 를 강화하는) 새로운 특성은 모두 Symbol 을 통해간단하고 안전하게 추가할 수 있습니다
##三.적용 장면
###1.DOM 요소에 마크 속성 추가
아마도 대부분의 경우 DOM 요소에 커스텀 속성을 추가할 필요는 없습니다. 부작용이 있기 때문입니다 (커스텀 속성명이 다른 코드와 충돌할 가능성, 또는 향후 DOM API 와 충돌할 가능성). 따라서 일반적으로 표 구조를 유지하여 요소에 대응하는 상태 등의 추가 정보를 저장하고, 매번 표를 조회하여 목표 요소의 추가 정보를 얻습니다. 표가 크면, 표 조회는 시간이 걸립니다. 표 구조가 복잡하면, 표 조회 조작 자체도 매우 번거로워집니다。。。等等, 왜 표를 조회하여 요소의 추가 정보를 얻어야 할까요? 부작용이 있기 때문입니다. 그렇다면, 이 부작용이 없어지면 어떻게 될까요?
Symbol 은 이 부작용을消除하는 데 사용됩니다. 요소 관련 정보는, 직접 Symbol 객체를 key 로 하여 DOM 요소에 추가하면 됩니다. 심플하고 난폭하고 안전하고 효과적입니다. 예를 들어:
var IS_MOVING = Symbol('isMoving');
var INFO = Symbol('info');
if (!elem[IS_MOVING]) {
anim(elem);
elem[IS_MOVING] = true;
elem[INFO] = {
step: 1;
};
}
커스텀의 Symbol 형의 key 를持有하고, 그 key 를 자신의 스코프에 두면, 부작용은 전혀 없어집니다
###2.유니크한 key 생성
이전에는 유니크한 key 를 생성하려면 조금 복잡한 알고리즘이 필요했지만, 현재 Symbol 은 가장 심플한 유니크 key 생성 방법입니다. 예를 들어:
// 不关注属性名,只是想简单地存取 obj
var data = {
dir: {},
save(val) {
var key = Symbol();
this.dir[key] = val;
return key;
},
get(key) {
return this.dir[key];
}
}
var key = data.save('data');
console.log(data.get(key)); // data
###3.네이티브 Symbol 을 통해 지원되는 API 확장 구현
표준은 점점 더 많은 hook 을 제공하여, API 확장 메서드를 구현할 수 있게 합니다. 예를 들어 커스텀 객체를 이터러블하게 합니다:
var obj = {
'a': 1,
'b': 2
}
// generator 实现迭代器
obj[Symbol.iterator] = function*() {
for (var key in this) {
yield this[key];
}
}
for (var val of obj) {
console.log(val);
}
##四.요약
Symbol 은 정교한 특성으로, 기존 API 를 확장하는 큰 문제를 해결했습니다 (새로운 특성에 일련의 hook 메커니즘을 제공하여, 향후 안전하게 확장할 수 있습니다)
###참고 자료
- 『ES6 in Depth』: InfoQ 中文站에서 제공하는 무료 전자책
아직 댓글이 없습니다