분류 Nodejs

JavaScript의 심볼에 대한 실용 안내서

컨텐츠 정보

  • 조회 298 (작성일 )

본문

JavaScript는 속성 이름 충돌을 방지하기 위해 ES6에서 심볼을 도입했습니다. 또한 보너스는 2015-2019 JavaScript에서 개인 속성을 시뮬레이션 하는 방법을 제공합니다.


소개 


JavaScript에서 심볼을 만드는 가장 간단한 방법은 Symbol() 함수를 호출하는 것입니다. 심볼을 특별하게 만드는 2 가지 주요 속성은 다음과 같습니다.

  1. 객체 키로 심볼을 사용할 수 있습니다. 문자열과 심볼만 객체 키로 사용할 수 있습니다.
  2. 두 개의 심볼은 동일하지 않습니다.
const symbol1 = Symbol();
const symbol2 = Symbol();

symbol1 === symbol2; // false

const obj = {};
obj[symbol1] = 'Hello';
obj[symbol2] = 'World';

obj[symbol1]; // 'Hello'
obj[symbol2]; // 'World'

Symbol() 호출로 인해 심볼이 객체처럼 보이지만 실제로는 심볼이 JavaScript의 기본 유형입니다. new를 사용하여 생성자로 Symbol을 사용하면 오류가 발생합니다.

const symbol1 = Symbol();

typeof symbol1; // 'symbol'
symbol1 instanceof Object; // false

// Throws "TypeError: Symbol is not a constructor"
new Symbol();

설명 


Symbol() 함수는 단일 매개 변수인 문자열 설명을 사용합니다. 심볼의 설명은 디버깅 목적으로만 사용됩니다. 설명은 심볼의 toString()에 표시됩니다. 그러나 동일한 설명을 가진 두 개의 심볼은 동일하지 않습니다.

const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');

symbol1 === symbol2; // false
console.log(symbol1); // 'Symbol(my symbol)'

글로벌 심볼 레지스트리도 있습니다. Symbol.for()를 사용하여 심볼을 만들면 심볼 설명에 따라 키를 전역 레지스트리에 추가합니다. 즉, Symbol.for()를 사용하여 동일한 설명으로 두 개의 심볼을 만들면 두 심볼이 동일합니다.

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'

일반적으로 명명 충돌이 발생할 수 있으므로 적절한 이유가 없는 한 전역 기호 레지스트리를 사용하면 안됩니다.


이름 충돌 


JavaScript의 첫 번째 기본 제공 심볼은 ES6의 Symbol.iterator 심볼입니다. Symbol.iterator 함수가 있는 객체는 반복 가능한 것으로 간주됩니다. 즉, 해당 객체를 for / of 루프의 오른쪽으로 사용할 수 있습니다.

const fibonacci = {
  [Symbol.iterator]: function*() {
    let a = 1;
    let b = 1;
    let temp;

    yield b;

    while (true) {
      temp = a;
      a = a + b;
      b = temp;
      yield b;
    }
  }
};

// Prints every Fibonacci number less than 100
for (const x of fibonacci) {
  if (x >= 100) {
    break;
  }
  console.log(x);
}

Symbol.iterator가 문자열이 아닌 심볼인 이유는 무엇입니까? Symbol.iterator를 사용하는 대신 iterable 스펙에서 문자열 특성 'iterator'가 있는지 확인했다고 가정하십시오. 또한 반복 가능 해야 하는 아래 클래스가 있다고 가정하십시오.

class MyClass {
  constructor(obj) {
    Object.assign(this, obj);
  }

  iterator() {
    const keys = Object.keys(this);
    let i = 0;
    return (function*() {
      if (i >= keys.length) {
        return;
      }
      yield keys[i++];
    })();
  }
}

MyClass의 인스턴스는 객체의 키를 반복 할 수 있는 반복 가능한 객체입니다. 그러나 위의 클래스에는 잠재적 인 결함이 있습니다. 악의적인 사용자가 반복자 속성을 가진 객체를 MyClass로 전달한다고 가정합니다.

const obj = new MyClass({ iterator: 'not a function' });

obj에 for / of를 사용하면 JavaScript에서 TypeError가 발생합니다. obj는 반복 가능하지 않습니다. 사용자 지정 반복자 함수가 클래스의 반복자 속성을 덮어 쓰기 때문입니다. 이는 사용자 데이터를 순진하게 복사하면 __proto__ 및 생성자와 같은 특수 속성에 문제가 발생할 수 있는 프로토 타입 오염과 유사한 보안 문제입니다.


여기서 핵심 패턴은 기호가 사용자 데이터와 객체의 프로그램 데이터를 명확하게 구분할 수 있다는 것입니다. JSON에서 기호를 표현할 수 없으므로 Symbol.iterator 속성이 잘못된 Express API로 데이터가 전달 될 위험이 없습니다. 사용자 데이터와 Mongoose 모델과 같은 내장 함수 및 메소드를 혼합하는 객체에서 기호를 사용하여 사용자 데이터가 내장 기능과 충돌하지 않도록 할 수 있습니다.


개인(Private) 속성 


두 개의 기호가 동일하지 않으므로 기호는 JavaScript에서 개인 속성을 시뮬레이션 하는 편리한 방법입니다. 심볼은 Object.keys ()에 표시되지 않으므로 심볼을 명시 적으로 내 보내지 않으면 명시 적으로 Object.getOwnPropertySymbols () 함수를 거치지 않으면 다른 코드에서 해당 속성에 액세스 할 수 없습니다.

function getObj() {
  const symbol = Symbol('test');
  const obj = {};
  obj[symbol] = 'test';
  return obj;
}

const obj = getObj();

Object.keys(obj); // []

// Unless you explicitly have a reference to the symbol, you can't access the
// symbol property.
obj[Symbol('test')]; // undefined

// You can still get a reference to the symbol using `getOwnPropertySymbols()`
const [symbol] = Object.getOwnPropertySymbols(obj);
obj[symbol]; // 'test'

JSON.stringify() 출력에 표시되지 않기 때문에 기호는 개인 속성에도 편리합니다. 특히 JSON.stringify()는 기호 키와 값을 자동으로 무시합니다.

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

계속 


심볼은 사용자 데이터가 프로그램 상태와 분리 된 상태를 유지하면서 개체의 내부 상태를 나타내는 데 유용한 도구입니다. 심볼을 사용하면 프로그램 상태 속성 앞에 '$'를 접두어로 붙이는 등의 규칙이 더 이상 필요하지 않습니다. 다음에 객체 속성을 $$ __ internalFoo로 설정하면 심볼을 대신 사용해보십시오.


http://thecodebarbarian.com/a-practical-guide-to-symbols-in-javascript.html