ES6에 Symbol을 가져 오는 동기는 무엇입니까?


368

업데이트 : 최근 Mozilla훌륭한 기사가 나왔습니다. 궁금하다면 읽어보십시오.

아시다시피 ECMAScript 6에 새로운 Symbol 프리미티브 유형 을 포함계획입니다 (다른 미친 것들은 말할 것도 없습니다). 나는 :symbol루비 의 개념이 불필요 하다고 항상 생각했다 . JavaScript 에서처럼 일반 문자열을 쉽게 사용할 수 있습니다. 그리고 이제 그들은 JS의 것들을 복잡하게하기로 결정했습니다.

나는 동기를 이해하지 못한다. 자바 스크립트에서 실제로 기호가 필요한지 설명해 줄 수 있습니까?


6
이 설명이 얼마나 확실한지 모르겠지만 시작은 tc39wiki.calculist.org/es6/symbols 입니다.
Felix Kling

8
기호는 너무 많은 것을 가능 하게하며 , 개체에 대해 범위가 지정된 고유 식별자를 허용합니다. 예를 들어 한 곳에서만 액세스 할 수있는 객체에 대한 속성이 있습니다.
Benjamin Gruenbaum

5
확실하지에 대한 당신이 Object.getOwnPropertySymbols (O)를 사용할 수 있기 때문에 것을
Yanis

4
프라이버시보다는 독창성입니다.
콴타스 94 헤비

2
그들은 더 간단한 클래스 구현을 위해 결정한 클래스 속성 키워드 privatepublic클래스 속성 키워드를 사용하여 더 복잡한 클래스 구현을 갖게 되었습니다. 대신에 this.x = x당신은 public x = x개인 변수를 위해 해야했습니다 private y = y. 그들은 훨씬 더 최소한의 클래스 구현을 위해 그것을 버리지 않기로 결정했습니다. 그런 다음 Symbol은 최소한의 구현으로 개인 속성을 가져 오는 데 필요한 해결 방법입니다.
lyschoening

답변:


224

Javascript에 심볼을 도입하려는 원래 동기는 개인 속성 을 활성화하는 것이 었습니다 .

불행하게도, 그들은 심각하게 다운 그레이드되었습니다. 예를 들어, Object.getOwnPropertySymbols프록시를 사용하여 리플렉션을 통해 찾을 수 있으므로 더 이상 비공개 입니다.

이제는 고유 한 기호 로 알려져 있으며 속성 간의 이름 충돌을 피하기위한 용도로만 사용됩니다. 예를 들어 ECMAScript 자체는 사용자 이름과 충돌 할 위험없이 객체에 배치 할 수있는 특정 방법 (예 : 반복 프로토콜 정의)을 통해 확장 후크를 도입 할 수 있습니다.

언어에 기호를 추가하려는 동기가 충분히 강력한 지 여부는 논란의 여지가 있습니다.


93
대부분의 언어 (모든 주류 언어)는 어쨌든 개인에게 접근 할 수있는 몇 가지 메커니즘 (일반적으로 반영)을 제공합니다.
Esailija

19
@Esailija, 나는 그것이 사실이라고 생각하지 않습니다. 특히 많은 언어가 처음에는 리플렉션을 제공하지 않기 때문입니다. Java와 같이 리플렉션을 통해 개인 상태를 유출하는 것은 기능이 아니라 버그로 간주되어야합니다. 이는 신뢰할 수있는 개인 상태를 갖는 것이 보안과 관련 될 수있는 웹 페이지에서 특히 그렇습니다. 현재 JS에서이를 달성 할 수있는 유일한 방법은 클로저를 이용하는 것인데, 이는 지루하고 비용이 많이들 수 있습니다.
Andreas Rossberg

38
C ++, Java, C #, Ruby, Python, PHP, Objective-C는 모두 원한다면 원하는 방식으로 액세스 할 수 있습니다. 그것은 능력에 관한 것이 아니라 의사 소통에 관한 것입니다.
Esailija

4
@plalx는 웹에서 캡슐화가 때로는 보안에 관한 것이기도합니다.
Andreas Rossberg

3
불행히도 @RolandPihlakas Object.getOwnPropertySymbols만이 유일한 누출은 아닙니다. 더 어려운 것은 프록시를 사용하여 "비공개"속성에 대한 액세스를 가로채는 기능입니다.
Andreas Rossberg

95

기호는 진정한 개인 정보 보호를 보장하지는 않지만 객체의 공용 속성과 내부 속성을 분리하는 데 사용할 수 있습니다. Symbol개인 속성을 갖는 데 사용할 수있는 예를 살펴 보겠습니다 .

객체의 속성이 비공개가 아닌 경우를 예로 들어 봅시다.

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

위의 Pet클래스 속성 type은 비공개가 아닙니다. 비공개로하려면 클로저를 만들어야합니다. 아래 예제 type는 클로저를 사용하여 비공개로 만드는 방법을 보여줍니다 .

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

위의 접근 방식의 단점 : Pet생성 된 각 인스턴스 에 대해 추가적인 폐쇄를 도입하여 성능을 저하시킬 수 있습니다.

이제 우리는 소개 Symbol합니다. 이를 통해 불필요한 추가 폐쇄를 사용하지 않고 개인 재산을 만들 수 있습니다. 아래 코드 예 :

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog

15
심볼 속성 은 비공개가 아닙니다 ! 심볼은 충돌이 없습니다 . 허용 된 답변을 읽을 수 있습니다.
Bergi

3
예, symbol은 진정한 개인 정보 보호를 보장하지는 않지만 객체의 공용 속성과 내부 속성을 분리하는 데 사용할 수 있습니다. 죄송합니다.이 점을 내 대답에 추가하는 것을 잊었습니다. 이에 따라 답변을 업데이트하겠습니다.
사마르 판다

@SamarPanda, 접두사 멤버로 _진정한 프라이버시를 보장하지는 않지만 객체의 공개 속성과 내부 속성을 분리하는 데 사용할 수 있다고 말할 수도 있습니다. 다시 말해, 무의미한 대답입니다.
Pacerier

10
기호는 기본적으로 열거 할 수 없으므로 '무시'로 액세스 할 수 없으며 다른 키는 액세스 할 수 없으므로 의미가 없습니다.
Patrick

5
나는 당신의 대답이 실제로 이해가되는 유일한 예를 찾았습니다. 왜 당신은 왜 일반적인 속성 대신 객체의 개인 속성을 Symbol로 정의하고 싶습니까?
Luis Lobo Borobia

42

Symbols객체에서 고유 한 속성 이름으로 사용할 수있는 새롭고 특별한 종류의 객체입니다. Symbol대신에를 사용 string하면 서로 다른 모듈이 서로 충돌하지 않는 속성을 만들 수 있습니다. Symbols은 (는) 비공개로 만들 수 있으므로 아직에 직접 액세스하지 않은 사람은 속성에 액세스 할 수 없습니다 Symbol.

Symbols새로운 프리미티브 입니다. 그냥 같은 number, string그리고 boolean프리미티브 Symbol를 생성하는 데 사용할 수있는 기능을 가지고있다. 다른 프리미티브와 달리 Symbols리터럴 구문은 없습니다 (예 : how stringhave '').이를 생성하는 유일한 방법 Symbol은 다음과 같은 방법으로 생성자를 사용하는 것입니다.

let symbol = Symbol();

실제로 Symbol는 객체에 속성을 첨부하는 약간 다른 방법 입니다 . 상속 된 모든 항목에 나타나는 Symbols것처럼 표준 메서드를 쉽게 제공 할 수 있습니다 .Object.prototype.hasOwnPropertyObject

다음은 Symbol기본 유형 의 장점 중 일부입니다 .

Symbols 디버깅 기능이 내장되어 있습니다

Symbols 콘솔에 로깅 할 때 인생을 조금 더 쉽게하기 위해 디버깅에 실제로 사용되는 설명을 제공 할 수 있습니다.

SymbolsObject키로 사용할 수 있습니다

이곳이 Symbol정말 흥미로워 지는 곳 입니다. 그것들은 물체와 크게 얽혀 있습니다. Symbol객체에 키로 할당 할 수 있습니다. 즉, 객체에 고유 한 수를 무제한으로 할당 할 수 Symbol있으며 string키 또는 다른 고유 한 객체와 충돌하지 않도록 보장 할 수 있습니다 Symbols.

Symbols 고유 한 값으로 사용할 수 있습니다.

하자가 같은 여러 개의 로그 수준을 포함하는 로깅 라이브러리가 가정 logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN등을. ES5 코드에서는 이것을 strings (so logger.levels.DEBUG === 'debug') 또는 numbers ( logger.levels.DEBUG === 10)로 만들고 싶습니다 . 이러한 값은 고유 한 값이 아니기 때문에 이상적이지 않지만 Symbols는 이상적입니다 ! 따라서 logger.levels간단히 다음과 같습니다.

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

위대한 기사 에서 더 많은 것을 읽으십시오 .


10
나는 당신의 모범을 이해하지 못하며 왜 log.levels = {DEBUG: Symbol('debug')단순히 당신이 필요 하지 않을까요 log.levels = {DEBUG:'debug'}? 결국 그것은 동일합니다. 객체의 키를 반복 할 때 Symbols가 보이지 않는다고 언급 할 가치가 있다고 생각합니다. 그것이 그들의 "것"입니다
vsync

한 가지 이점은 누군가가 실수 로 리터럴을 사용할 수 없으며 그것이 영원히 작동한다고 믿는다는 것입니다. (단순히 {}동일한 결과 (독특한 값으로) 를 사용 하고 달성 할 수 있거나 프로젝트에서 리터럴이 선호되거나 문서를 먼저 읽어야한다고 말할 수 있기 때문에 이것은 실제로 강력한 논쟁이 아닙니다 .) 개인적으로 이것이 코드에서 고유 한 의미에 대한 가독성이 뛰어나다 고 생각합니다
Apple apple

고유 한 값으로 사용시주의 사항, 객체 리터럴 또한이 내장 debuggability을Symbol("some message")되고 {message:'some message'}여러 필드를 추가 할 수 있기 때문에 틀림없이 객체는 여기에 더 나은 않습니다.
사과 사과

38

이 게시물은 Symbol()내가 찾거나 만들 수있는 실제 예제와 내가 찾을 수있는 사실 및 정의와 함께 제공됩니다.

TLDR;

Symbol()ECMAScript를 6 (ES6)의 방출 도입 데이터 형식이다.

Symbol에 대한 두 가지 흥미로운 사실이 있습니다.

  • 리터럴이없는 JavaScript의 첫 번째 데이터 유형 및 유일한 데이터 유형

  • 로 정의 된 모든 변수 Symbol()는 고유 한 콘텐츠를 얻지 만 실제로는 비공개 가 아닙니다 .

  • 모든 데이터에는 자체 Symbol 이 있으며 동일한 데이터에 대해 Symbols는 동일 합니다. 다음 단락에서 더 많은 정보, 그렇지 않으면 TLRD가 아닙니다. :)

심볼을 어떻게 초기화합니까?

1. 디버깅 가능한 값을 가진 고유 식별자를 얻으려면

이 방법으로 할 수 있습니다 :

var mySymbol1 = Symbol();

또는이 방법 :

var mySymbol2 = Symbol("some text here");

"some text here"문자열이 기호에서 추출 할 수 없습니다, 그것은 디버깅 목적으로 만 설명입니다. 어떤 식 으로든 기호 동작을 변경하지 않습니다. 비록 당신이 할 수는 console.log있지만 (값은 디버깅을위한 것이므로 다른 로그 항목으로 그 로그를 착각하지 않도록)

console.log(mySymbol2);
// Symbol(some text here)

2. 일부 문자열 데이터에 대한 기호를 얻으려면

이 경우 심볼의 값이 실제로 고려되며이 방식으로 두 심볼이 고유하지 않을 수 있습니다.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

이러한 기호를 "두 번째 유형"기호라고합니다. Symbol(data)어떤 식 으로든 "첫 번째 유형"기호 (예 :로 정의 된 기호)와 교차하지 않습니다 .

다음 두 단락은 첫 번째 유형 기호 에만 해당 됩니다.

이전 데이터 유형 대신 Symbol을 사용하면 어떤 이점이 있습니까?

먼저 표준 데이터 유형 인 객체를 고려해 봅시다. 키-값 쌍을 정의하고 키를 지정하여 값에 액세스 할 수 있습니다.

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

Peter라는 이름을 가진 두 사람이 있다면 어떨까요?

이것을하는 것 :

var persons = {"peter":"first", "peter":"pan"};

별로 이해가되지 않습니다.

따라서 같은 이름을 가진 두 명의 완전히 다른 사람의 문제인 것 같습니다. 그런 다음 new를 참조하십시오 Symbol(). 실생활의 사람 과 같습니다. 모든 사람은 독특 하지만 이름은 동일 할 수 있습니다. 두 "사람"을 정의합시다.

 var a = Symbol("peter");
 var b = Symbol("peter");

이제 같은 이름을 가진 두 사람이 있습니다. 우리의 사람들은 실제로 다른가? 그들은; 당신은 이것을 확인할 수 있습니다 :

 console.log(a == b);
 // false

우리는 어떻게 유익합니까?

우리는 다른 사람을 위해 당신의 물건에 두 개의 항목을 만들 수 있으며 어떤 식 으로든 착각 할 수 없습니다.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

참고 :
객체를 문자열 화하면 JSON.stringifySymbol로 키로 초기화 된 모든 쌍이 삭제됩니다.
실행 Object.keys은 그러한 Symbol()->value쌍을 반환하지 않습니다 .

이 초기화를 사용하면 첫 번째 사람과 두 번째 사람의 항목을 착각하는 것은 절대 불가능합니다. 전화 console.log하면 두 번째 이름이 올바르게 출력됩니다.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

객체에 사용될 때 열거 불가능한 속성을 정의하는 것과 어떻게 다른가요?

실제로, 숨겨 Object.keys지고 열거 될 속성을 정의하는 방법이 이미 존재했습니다 . 여기있어:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

어떤 차이가 Symbol()있습니까? 차이점은 Object.defineProperty일반적인 방법으로 속성을 정의 할 수 있다는 것입니다.

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

그리고 이전 단락에서와 같이 Symbol로 정의 된 경우 :

fruit = Symbol("apple");

변수를 알고있는 경우에만 값을받을 수 있습니다.

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

또한 키 아래에 다른 속성을 정의 "apple" 하면 개체가 오래된 개체를 삭제하게됩니다 (하드 코딩 된 경우 오류가 발생할 수 있음). 더 이상 사과는 없습니다! 그 유감. 이전 단락을 참조하면 기호는 고유하며 Symbol()고유하게 키를 정의합니다 .

타입 변환 및 확인

  • 다른 데이터 형식과 달리 Symbol() 합니다.

  • 호출하여 원시 데이터 유형을 기반으로 심볼을 "만들"수 있습니다 Symbol(data) .

  • 유형 확인 측면에서 아무 것도 변경되지 않습니다.

    function isSymbol ( variable ) {
        return typeof someSymbol === "symbol";
    }
    
    var a_Symbol = Symbol("hey!");
    var totally_Not_A_Symbol = "hey";
    
    console.log(isSymbol(a_Symbol)); //true
    console.log(isSymbol(totally_Not_A_Symbol)); //false


SO 문서에서 마이그레이션 되었습니까?
Knu

1
@KNU는 아니었다. 정보를 수집하고이 답변을 직접 작성했습니다
nicael

정말 아름다운 답변입니다!
Mihai Alexandru-Ionut

1
Symbol에 대한 훌륭한 대답이지만 여전히 배열 대신 기호 키가있는 객체를 사용하는 이유를 모르겠습니다. { "peter": "pan"} { "john": "doe"} 같은 사람이 여러 명인 경우 하나의 개체에 넣는 것은 기분이 좋지 않습니다. personFirstName1, personFirstName2와 같은 속성이 중복 된 클래스를 만들지 않는 것과 같은 이유로. 이것은 그것을 문자열 화 할 수없는 능력과 결합하여 이점을 단점이라고 볼 수는 없습니다.
eldo

18

내가 보는 방법은 다음과 같습니다. 심볼은 Object.keys () 및 JSON.stringify ()와 같이 널리 사용되는 일부 메서드를 통해 객체의 키 / 속성이 노출되지 않도록하여 '추가 수준의 개인 정보 보호'를 제공합니다.

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

객체 자체가 제공되지만 이러한 속성은 리플렉션, 프록시, Object.getOwnPropertySymbols () 등을 통해 여전히 노출 될 수 있지만 몇 가지 직접 메소드를 통해 액세스 할 수있는 자연스러운 방법은 없습니다. 때로는 OOP 관점에서 충분할 수도 있습니다.


2

JS 심볼은 새로운 기본 데이터 유형입니다. 이것은 고유 한 ID 역할을하는 토큰입니다 . Symbol생성자를 사용하여 심볼을 만들 수 있습니다 . 예를 들어 MDN의 다음 코드를 보자.

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

다음과 같이 심볼을 고유 한 객체 속성 키로 사용하는 것이 종종 편리합니다.

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123


0

기호에는 두 가지 주요 사용 사례가 있습니다.

  1. "숨겨진"객체 속성. 다른 스크립트 나 라이브러리에 포함 된 객체에 속성을 추가하려는 경우 심볼을 만들어 속성 ​​키로 사용할 수 있습니다. 기호 속성은에 표시되지 않으므로 for..in실수로 다른 속성과 함께 처리되지 않습니다. 또한 다른 스크립트에는 심볼이 없으므로 직접 액세스 할 수 없습니다. 따라서이 속성은 실수로 사용하거나 덮어 쓰지 않도록 보호됩니다.

    그래서 우리는 상징적 인 속성을 사용하여 우리가 필요로하는 물체에 무언가를 숨기고 숨길 수 있습니다.

  2. 로 액세스 할 수있는 JavaScript에서 사용되는 많은 시스템 심볼이 있습니다 Symbol.*. 우리는 그것들을 사용하여 내장 동작을 변경할 수 있습니다. 예를 들어, .... Symbol.iteratoriterables의 경우 Symbol.toPrimitive객체 대 기본 변환 등을 설정합니다.

출처

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.