본문 바로가기
Programing/Javascript

[JavaScript] Symbol 에 대한 궁극적인 가이드(Symbol Guide)

by 멍멍돌이야 2022. 6. 29.
반응형

The Ultimate Guide to JavaScript Symbol

(JavaScript Symbol 에 대한 궁극적인 가이드)

 

Creating symbols

ES6은 새로운 기본 유형으로 Symbol을 추가했습니다. number, boolean, null, undefined, string과 같은 다른 기본 유형과 달리 Symbol() 유형에는 리터럴 형식이 없습니다.

 

 

Symbol를 만들려면 다음 예제와 같이 전역 Symbol() 함수를 사용합니다.

let s = Symbol('foo');

Symbol() 함수는 호출할 때마다 새로운 고유 값을 생성합니다.

 

console.log(Symbol() === Symbol()); // false

Symbol() 함수는 설명을 선택적 인수로 받아들입니다. 설명 인수는 Symbol을 더 설명적으로 만듭니다.

 

다음 예제에서는 firstName과 lastName이라는 두 개의 Symbol을 만듭니다.

let firstName = Symbol('first name'),
    lastName = Symbol('last name');

toString() 메서드를 사용하여 Symbol의 description 속성에 액세스할 수 있습니다.

console.log() 메서드는 다음 예제와 같이 암시적으로 Symbol의 toString() 메서드를 호출합니다.

console.log(firstName); // Symbol(first name)
console.log(lastName); // Symbol(last name)

Symbol는 기본 값이기 때문에 typeof 연산자를 사용하여 변수가 Symbol인지 확인할 수 있습니다. 

Symbol 변수를 전달할 때 Symbol 문자열을 반환하는 ES6 확장 typeof:

console.log(typeof firstName); // symbol

Symbol는 기본 값이므로 다음을 사용하여 Symbol를 생성하려고 하면 new연산자를 사용하면 오류가 발생합니다.

let s = new Symbol(); // error

 

Sharing symbols (공유)

ES6은 전역적으로 Symbol를 공유할 수 있는 전역 Symbol 레지스트리를 제공합니다. 공유할 심볼을 생성하려면 Symbol() 함수를 호출하는 대신 Symbol.for() 메서드를 사용합니다.

 

 

Symbol.for() 메서드는 다음 예제와 같이 Symbol 설명에 사용할 수 있는 단일 매개 변수를 허용합니다.

let ssn = Symbol.for('ssn');

Symbol.for() 메서드는 먼저 전역 Symbol 레지스트리에서 ssn 키를 사용하여 Symbol를 검색합니다. 기존 Symbol가 있는 경우 이를 반환합니다. 그렇지 않으면 Symbol.for() 메서드는 새 Symbol를 만들고 지정된 키를 사용하여 전역 Symbol 레지스트리에 등록한 다음 Symbol를 반환합니다.

 

 

나중에 같은 키를 사용하여 Symbol.for() 메서드를 호출하면 Symbol.for() 메서드가 기존 Symbol를 반환합니다.

let citizenID = Symbol.for('ssn');
console.log(ssn === citizenID); // true

이 예에서는 Symbol.for() 메서드를 사용하여 ssn 키로 Symbol를 조회했습니다. 전역 Symbol 레지스트리에 이미 포함되어 있기 때문에 Symbol.for() 메서드는 Symbol를 반환했습니다.

 

 

Symbol와 연결된 키를 가져오려면 다음 예제와 같이 Symbol.keyFor() 메서드를 사용합니다.

console.log(Symbol.keyFor(citizenID)); // 'ssn'

 

전역 Symbol 레지스트리에 없는 Symbol인 경우 System.keyFor() 메서드는 정의되지 않음을 반환합니다.

let systemID = Symbol('sys');
console.log(Symbol.keyFor(systemID)); // undefined

 

Symbol usages (사용법)

A) Using symbols as unique values (Symbol를 고유한 값으로 사용)

코드에서 문자열이나 숫자를 사용할 때마다 Symbol를 대신 사용해야 합니다. 예를 들어 작업 관리 애플리케이션에서 상태를 관리해야 합니다. ES6 이전에는 작업의 다양한 상태를 나타내기 위해 열림, 진행 중, 완료됨, 취소됨 및 보류와 같은 문자열을 사용했습니다. ES6에서는 다음과 같이 Symbol를 사용할 수 있습니다.

let statuses = {
    OPEN: Symbol('Open'),
    IN_PROGRESS: Symbol('In progress'),
    COMPLETED: Symbol('Completed'),
    HOLD: Symbol('On hold'),
    CANCELED: Symbol('Canceled')
};
// complete a task
task.setStatus(statuses.COMPLETED);

 

B) Using symbol as the computed property name of an object(객체의 계산된 속성 이름으로 Symbol 사용)

Symbol를 계산된 속성 이름으로 사용할 수 있습니다. 다음 예를 참조하십시오.

let status = Symbol('status');
let task = {
    [status]: statuses.OPEN,
    description: 'Learn ES6 Symbol'
};
console.log(task);

 

개체의 모든 열거 가능한 속성을 얻으려면 Object.keys() 메서드를 사용합니다.

console.log(Object.keys(task)); // ["description"]

속성이 열거 가능한지 여부에 관계없이 개체의 모든 속성을 가져오려면 Object.getOwnPropertyNames() 메서드를 사용합니다.

console.log(Object.getOwnPropertyNames(task)); // ["description"]

객체의 모든 속성 Symbol를 가져오려면 ES6에 추가된 Object.getOwnPropertySymbols() 메서드를 사용합니다.

console.log(Object.getOwnPropertySymbols(task)); //[Symbol(status)]

Object.getOwnPropertySymbols() 메서드는 객체에서 고유한 속성 Symbol의 배열을 반환합니다.

 

 

 

Well-known symbols (잘 알려진 Symbol)

ES6은 잘 알려진 Symbol라고 하는 미리 정의된 Symbol를 제공합니다. 잘 알려진 Symbol는 JavaScript의 일반적인 동작을 나타냅니다. 잘 알려진 각 Symbol는 Symbol 개체의 정적 속성입니다.

 

Symbol.hasInstance

 

Symbol.hasInstance는 instanceof 연산자의 동작을 변경하는 Symbol입니다. 일반적으로 instanceof 연산자를 사용할 때:

obj instanceof type;

JavaScript는 다음과 같이 Symbol.hasIntance 메서드를 호출합니다.

 
type[Symbol.hasInstance](obj);

그런 다음 obj가 유형 개체의 인스턴스인지 확인하는 메서드에 따라 다릅니다. 다음 예를 참조하십시오.

class Stack {

}
console.log([] instanceof Stack); // false

[ ] 배열은 Stack 클래스의 인스턴스가 아니므로 이 예제에서 instanceof 연산자는 false를 반환합니다.

[] 배열이 Stack 클래스의 인스턴스가 되기를 원한다고 가정하면 다음과 같이 Symbol.hasInstance 메서드를 추가할 수 있습니다.

class Stack {
    static [Symbol.hasInstance](obj) {
        return Array.isArray(obj);
    }
}
console.log([] instanceof Stack); // true

 

Symbol.iterator

Symbol.iterator는 함수가 객체에 대한 반복자를 반환할지 여부를 지정합니다.
Symbol.iterator 속성이 있는 객체를 iterable 객체라고 합니다.
ES6에서 모든 컬렉션 객체(Array, Set, Map)와 문자열은 반복 가능한 객체입니다.
ES6은 다음 예제와 같이 iterable 객체와 함께 작동하는 for…of 루프를 제공합니다.

 
var numbers = [1, 2, 3];
for (let num of numbers) {
    console.log(num);
}

// 1
// 2
// 3

내부적으로 JavaScript 엔진은 먼저 숫자 배열의 Symbol.iterator 메서드를 호출하여 반복자 개체를 가져옵니다. 그런 다음 iterator.next() 메서드를 호출하고 iterator 객체의 값 속성을 num 변수에 복사합니다. 세 번의 반복 후에 결과 개체의 done 속성이 true가 되고 루프가 종료됩니다.

다음과 같이 System.iterator Symbol를 통해 기본 반복기 개체에 액세스할 수 있습니다.

var iterator = numbers[Symbol.iterator]();

console.log(iterator.next()); // Object {value: 1, done: false}
console.log(iterator.next()); // Object {value: 2, done: false}
console.log(iterator.next()); // Object {value: 3, done: false}
console.log(iterator.next()); // Object {value: undefined, done: true}

기본적으로 컬렉션은 반복할 수 없습니다. 그러나 다음 예제와 같이 Symbol.iterator를 사용하여 반복 가능하게 만들 수 있습니다.

class List {
    constructor() {
        this.elements = [];
    }

    add(element) {
        this.elements.push(element);
        return this;
    }

    *[Symbol.iterator]() {
        for (let element of this.elements) {
            yield  element;
        }
    }
}

let chars = new List();
chars.add('A')
     .add('B')
     .add('C');

// because of the Symbol.iterator
for (let c of chars) {
    console.log(c);
}

// A
// B
// C

 

Symbol.isConcatSpreadable

두 배열을 연결하려면 다음 예제와 같이 concat() 메서드를 사용합니다.

 
let odd  = [1, 3],
    even = [2, 4];
let all = odd.concat(even);
console.log(all); // [1, 3, 2, 4]

이 예에서 결과 배열에는 두 배열의 단일 요소가 포함됩니다. 또한 concat() 메서드는 아래와 같이 배열이 아닌 인수도 허용합니다.

 
let extras = all.concat(5);
console.log(extras); // [1, 3, 2, 4, 5]

숫자 5는 배열의 다섯 번째 요소가 됩니다.

위의 예에서 볼 수 있듯이 concat() 메서드에 배열을 전달할 때 concat() 메서드는 배열을 개별 요소로 퍼뜨립니다. 그러나 단일 기본 인수를 다르게 취급합니다. ES6 이전에는 이 동작을 변경할 수 없었습니다.

이것이 Symbol.isConcatSpreadable Symbol가 작동하는 이유입니다.

Symbol.isConcatSpreadable 속성은 concat() 함수의 결과에 개체를 개별적으로 추가할지 여부를 결정하는 부울 값입니다.

 

다음 예를 고려하십시오.

let list = {
    0: 'JavaScript',
    1: 'Symbol',
    length: 2
};
let message = ['Learning'].concat(list);
console.log(message); // ["Learning", Object]

목록 개체는 ['Learning'] 배열에 연결됩니다. 그러나 개별 요소는 확산되지 않습니다.

concat() 메서드에 전달할 때 배열에 개별적으로 추가된 목록 개체의 요소를 활성화하려면 다음과 같이 목록 개체에 Symbol.isConcatSpreadable 속성을 추가해야 합니다.

let list = {
    0: 'JavaScript',
    1: 'Symbol',
    length: 2,
    [Symbol.isConcatSpreadable]: true
};
let message = ['Learning'].concat(list);
console.log(message); // ["Learning", "JavaScript", "Symbol"]

Symbol.isConcatSpreadable의 값을 false로 설정하고 목록 개체를 concat() 메서드에 전달하면 전체 개체로 배열에 연결됩니다.

 

Symbol.toPrimitive

Symbol.toPrimitive 메소드는 객체가 기본 값으로 변환될 때 어떤 일이 발생해야 하는지를 결정합니다.

JavaScript 엔진은 각 표준 유형의 프로토타입에 Symbol.toPrimitive 메소드를 정의합니다.

Symbol.toPrimitive 메소드는 "숫자", "문자열" 및 "기본값"의 세 가지 값 중 하나를 갖는 힌트 인수를 사용합니다. 힌트 인수는 반환 값의 유형을 지정합니다. 힌트 매개변수는 객체가 사용되는 컨텍스트를 기반으로 JavaScript 엔진에 의해 채워집니다.

다음은 Symbol.toPrimitive 메소드를 사용하는 예입니다.

function Money(amount, currency) {
    this.amount = amount;
    this.currency = currency;
}
Money.prototype[Symbol.toPrimitive] = function(hint) {
    var result;
    switch (hint) {
        case 'string':
            result = this.amount + this.currency;
            break;
        case 'number':
            result = this.amount;
            break;
        case 'default':
            result = this.amount + this.currency;
            break;
    }
    return result;
}

var price = new Money(799, 'USD');

console.log('Price is ' + price); // Price is 799USD
console.log(+price + 1); // 800
console.log(String(price)); // 799USD

이 자습서에서는 JavaScript Symbol와 고유 값 및 개체 속성에 Symbol를 사용하는 방법을 배웠습니다. 또한 잘 알려진 Symbol를 사용하여 객체 동작을 수정하는 방법을 배웠습니다.

 

 

 

 

Reference: https://www.javascripttutorial.net/es6/symbol/
728x90
반응형

댓글