본문으로 건너뛰기

Symbol_ES6 노트 7

무료2016-05-08#JS#es6 symbol#js symbol#js symbol是什么

Symbol 은 정교한 특성으로, 기존 API 를 확장하는 큰 문제를 해결했다

##一.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.hasInstanceinstanceof 를 확장

  • 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 中文站에서 제공하는 무료 전자책

댓글

아직 댓글이 없습니다

댓글 작성