(ES6) - 17 ~ 19 Symbol 오브젝트, 프로퍼티, 메서드

  • SUNGMIN SHIN
  • 22 Minutes
  • 2017년 11월 22일

[ ECMA Script6 ]

(http://book.naver.com/bookdb/book_detail.nhn?bid=11556385)


symbol 오브젝트


1> ES6에서 추가 된 7번째 프리미티브(primitive, 원시형) 타입

symbol은 프리미티브 값으로 String, Number등의 다른 프리미티브 값과 같이 래퍼 오브젝트(Symbol)를 가지고 있습니다. 그리고 이 래퍼 오브젝트에는 프리미티브 값을 처리하기 위한 메서드와 프로퍼티가 존재합니다.
(참조 타입과 원시 타입 - http://codingnuri.com/javascript-tutorial/javascript-primitive-types-and-reference-types.html)

2> 프로그램 전체를 통해 유일한 값을 생성

3> symbol 값의 생성

const sym = Symbol([discription]);

Symbol값의 생성은 위의 형태로 작성하며 [discription]은 생성한 symbol에 대한 설명으로 선택적으로 사용합니다.

4> symbol 값의 출력

1
2
3
4
const sym = Symbol('심볼');
console.log(sym); // symbol('심볼')
console.log(sym == Symbol('심볼')); // false
console.log(sym === Symbol('심볼')); // false

symbol을 통해 생성된 값은 변경, 확인이 불가능합니다.

5> symbol 값의 변경

symbol값은 변경할 수 없습니다.


symbol 활용


1> 오브젝트에서 Symbol 사용

유일한 값을 갖는 symbol의 특성을 활용하여 symbol값을 오브젝트의 프로퍼티 키로 사용하면 중복되지 않는 프로퍼티 키가 되는 데 이러한 형태를 symbol-keyed property라고 하며 objectName[Symbol([discription])]의 형태로 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const sym = Symbol('123');
let obj = {
[sym]: '456'
};

console.log(sym); // Symbol(123)
console.log(obj[sym]); // '456'
console.log(obj.sym); // undefined

// symbol-keyed property 형태의 프로퍼티는 for-in문에서 열거되지 않음
for(let val in obj) {
console.log(value); // undefined
}

위의 예제에서 처럼 symbol-keyed property는 for-in문을 통해 열거되지 않습니다. ([[Enumerable]]:false 이기 때문이며 Object.getOwnProperty.Symbols()를 사용하여 열거할 수 있습니다.)

2> class의 method 이름으로 symbol 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const symbolOne = Symbol('symbol one');
const symbolTwo = Symbol('symbol two111');

class Sports {
static [symbolOne]() {
return 'Symbol-1';
}
[symbolTwo](){
return 'Symbol-2';
}
}

console.log(Sports[symbolOne]());

let obj = new Sports();
console.log(obj[symbolTwo]());

오브젝트에서와 마찬가지로 ClassNameSymbol([discription])의 형태로 사용합니다.

3> JSON.stringfy()에서 Symbol 사용

JSON.stringfy()를 실행하면 symbol-keyed property는 출력되지 않습니다. 이는 Symbol값을 외부에 노출되지 않도록 하기 위한 조치이며 불완전하지만 Object.getOwnPropertySymbols()를 사용하여 대처할 수 있습니다.

1
2
3
4
5
6
7
let sym = Symbol('key');
let obj = {
[sym]: 'value'
};
let result = JSON.stringfy(obj);

console.log(result);


symbol properties


1> well-known Symbol(표준심볼, 잘 알려진 심볼)

well-known Symbol은 스펙에서 처리 알고리즘을 구분하기 위해 부여한 이름으로 즉 JS 엔진이 디폴트로 퍼리하는 알고리즘 유형의 이름 입니다.(처음부터 시스템에 존재하는(엔진이 가지고 있는) 상수형태의 심볼)
스펙에서는 Symbol.iterator가 아닌 @@interator로 작성되어 있는 데 이것은 즉 @@ == Symbol 입니다.

프로그램에 같은 이름의 well-Known symbol을 작성하면 엔진의 디폴트 처리를 하지 않고 작성된 코드를 실행합니다(well-known symbol이 오버라이드 되는 것)

표준 심볼(Well-known Symbol)로써 사용
ES6가 등장하기 전에 사람들의 필요에 의해 생성하거나 개발한 함수들이 많을 것이다.
그 중에 공통된 함수들은 기능과 네이밍이 비슷한 경우가 많을 것이다. (내부 로직은 다를지라도)
이렇게 많은 사람들이 쓴 함수의 이름 등등을 표준 프로퍼티로 만들고 나면?
내부 로직이 다 같지는 않으므로 그 수많은 코드가 깨질 수 있다. (우리 Array.isArray와 마찬가지로 말이다.)
따라서 기존 개발자들이 개발한 코드들의 안전성을 보장하고자 표준 프로퍼티를 심볼로 만든 경우가 있다.
그 목록은 아래와 같다.
Symbol.iterator(추후 다룰 예정)
Symbol.unscopables
Symbol.match
Symbol.species
Symbol.toPrimitive
등등…
출처 (https://perfectacle.github.io/2017/04/16/ES6-Symbol/)

2> toStringTag

[object Object] 형태에서 ‘Object’를 symbol.toStringTag 값으로 표시 합니다.

1
2
3
4
5
6
7
let Sports = function(){};
let sportsObj = new Sports();

console.log(sportsObj.toString()); // [object Object]

Sports.prototype[Symbol.toStringTag] = 'Sports-Function';
console.log(sportsObj.toString()); // [object-Sports-Function]

아래 예제와 같이 class에서의 getter로도 작성할 수 있습니다.

1
2
3
4
5
6
7
8
class Sports{
get [Symbol.toStringTag]() {
return 'Sports-Function'
}
};

let sportsObj = new Sports();
console.log(sportsObj.toString());

3> isConcatSpreadable

Array 오브젝트의 concat()에서 배열을 결합할 때 결합하는 배열의 펼침 여부를 지정
(concat()은 인자로 주어진 배열이나 값들을 기존배열에 합쳐서 새 배열로 반환하는 메서드 - https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)

Symbol.isConcatSpreadable = Boolean(기본 값은 true)

1
2
3
4
5
6
7
8
9
let one = [11, 12];
let two = [21, 22];
let result = one.concat(two);
// [Symbol.isConcatSpreadable] = true(default)
console.log(result, result.length); // [11, 12, 21, 22] 4

two[Symbol.isConcatSpreadable] = false;
result = one.concat(two);
console.log(result, result.length); // [11, 12, Array[2]] 3

isConcatSpreadable의 프로퍼티로만 사용할 수 있고 함수로 사용할 수 없으며 Array-like 오브젝트 및 상속받은 class에 사용할 수 있습니다.

Array-like에서의 사용 예

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let one = [11, 12];
let fiveSix = {
0: 'five',
1: 'six',
length: 2
};
let result = one.concat(fiveSix);
// Array-like에서 isConcatSpreadable의 값은 기본이 false인 듯
console.log(result, result.length);


let arrayLike = {
[Symbol.isConcatSpreadable]: true,
0: 'five',
1: 'six',
length: 2
};
result = one.concat(arrayLike);
console.log(result, result.length);

4> unscopables

with문에서 사용되며 값이 true라면 프로퍼티를 전개하지 않습니다.(with문의 사용을 권장하지 않는 내용도 있으니 참고 - http://unikys.tistory.com/304)

Symbol.unscopables = Boolean(기본 값은 false)

ES5의 ‘use strict’ 범위와 ES6환경에서는 with문을 사용하면 에러가 발생합니다.
(Uncaught SyntaxError: Strict mode code may not include a with statement)

5> species

Symbol.species는 constructor를 반환합니다.
(여기서 constructor를 반환한다는 것은 constructor로 인스턴스를 생성하여 반환하는 것과 같은 이야기)

Symbol.species는 오버라이드 할 수 있으며 개발자 코드로 변환되는 인스턴스를 변경할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ExtendArray extends Array {
static get[Symbol.species]() {
return Array;
}
}

let oneInstance = new ExtendArray(1, 2, 3);
let twoInstance = oneInstance.slice(1,2);

console.log(oneInstance instanceof ExtendArray);

console.log(twoInstance instanceof Array);
console.log(twoInstance instanceof ExtendArray);

console.dir(oneInstance);
console.dir(twoInstance);

symbol.for

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for