자 바스 크 립 트 에 왜 Symbol 형식 이 있 는 지 자세히 알 아 보 세 요.

11467 단어 JavaScriptSymbol
Symbols 는 ES6 에서 새로운 데이터 형식 을 도입 하여 JS 에 좋 은 점 을 가 져 왔 습 니 다.특히 대상 속성 이 있 을 때.하지만 문자열 이 할 수 없 는 일 을 해 줄 수 있 을 까?
Symbol 을 깊이 연구 하기 전에 자바 스 크 립 트 의 특성 을 살 펴 보 자.많은 개발 자 들 이 이런 특성 을 모 를 수도 있다.
배경
js 중의 데이터 유형 은 전체적으로 두 가지 로 나 뉘 는데 그들 은 각각 값 유형 과 인용 유형 이다.
값 형식(기본 형식):수치 형(Number),문자 형식(String),불 값 형(Boolean),null,underfined
참조 형식(클래스):함수,대상,배열 등
값 형식 이해:변수 간 의 상호 할당 은 새로운 메모리 공간 을 열 고 변 수 를 새로운 변수 에 저장 하 는 것 을 말 합 니 다.그 후에 두 변수의 값 변동 은 서로 영향 을 주지 않 습 니 다.예 를 들 어:

var a = 10; //            a  “10”;
var b = a; //    b           ,  a    “10”             ;
//a   b           ,          ;
일부 언어,예 를 들 어 C 는 전달 과 값 전달 을 인용 하 는 개념 이 있다.자 바스 크 립 트 도 전달 하 는 데이터 형식 에 따라 비슷 한 개념 을 가지 고 있다.함수 에 값 을 전달 하면 이 값 을 다시 분배 합 니 다.호출 위치의 값 을 수정 하지 않 습 니 다.단,인용 형식 을 수정 하면 수 정 된 값 도 호출 된 곳 에서 수 정 됩 니 다.
인용 유형 이해:변수 간 의 상호 할당 값 은 포인터 의 교환 일 뿐 대상(일반 대상,함수 대상,배열 대상)을 새로운 변수 에 복사 하 는 것 이 아 닙 니 다.대상 은 여전히 하나 뿐 입 니 다.안내 가 하나 더 있 을 뿐 입 니 다~~예 를 들 면:

var a = { x: 1, y: 2 }; //            ,   a        ,             ;
var b = a; //  a          b,                     ;
//        a         ,    b                 ;
값 형식(신비 한 NaN 값 제외)은 항상 같은 값 을 가 진 다른 값 형식 과 똑 같 습 니 다.다음 과 같 습 니 다.

const first = "abc" + "def";
const second = "ab" + "cd" + "ef";
console.log(first === second); // true
그러나 똑 같은 구조의 인용 유형 은 다르다.

const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };
console.log(obj1 === obj2); // false
//   ,    .name        :
console.log(obj1.name === obj2.name); // true
대상 은 자 바스 크 립 트 언어 에서 중요 한 역할 을 하 는데,그들의 사용 은 어디 에 나 있다.대상 은 보통 키/값 쌍 의 집합 으로 사용 되 지만,이러한 방식 으로 사용 하 는 것 은 매우 큰 제한 이 있 습 니 다.symbol 이 나타 나 기 전에 대상 키 는 문자열 일 수 있 습 니 다.비 문자열 값 을 대상 으로 하 는 키 를 사용 하려 면 이 값 은 문자열 로 강제로 변 환 됩 니 다.다음 과 같 습 니 다.

const obj = {};
obj.foo = 'foo';
obj['bar'] = 'bar';
obj[2] = 2;
obj[{}] = 'someobj';
console.log(obj);
// { '2': 2, foo: 'foo', bar: 'bar',
   '[object Object]': 'someobj' }
심 볼 이 뭐야?
Symbol()함 수 는 symbol 형식의 값 을 되 돌려 줍 니 다.이 유형 은 정적 속성 과 정적 방법 을 가지 고 있 습 니 다.그것 의 정적 속성 은 몇 개의 내 장 된 구성원 대상 을 폭로 합 니 다.정적 방법 은 전역 적 인 symbol 등록 을 드 러 내 고 내장 대상 클래스 와 유사 하지만 구조 함수 로 서 완전 하지 않 습 니 다.문법 인'new Symbol()'을 지원 하지 않 기 때 문 입 니 다.그래서 Symbol 을 사용 하여 생 성 된 값 은 같 지 않 습 니 다.

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
symbol 을 예화 할 때 선택 할 수 있 는 첫 번 째 인자 가 있 습 니 다.문자열 을 제공 하 는 것 을 선택 할 수 있 습 니 다.이 값 은 코드 를 디 버 깅 하 는 데 사 용 됩 니 다.그렇지 않 으 면 symbol 자체 에 영향 을 주지 않 습 니 다.

const s1 = Symbol("debug");
const str = "debug";
const s2 = Symbol("xxyy");
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
symbol 대상 속성
symbol 은 또 다른 중요 한 용도 가 있 습 니 다.대상 의 키 로 사용 할 수 있 습 니 다.다음 과 같 습 니 다.

const obj = {};
const sym = Symbol();
obj[sym] = "foo";
obj.bar = "bar";
console.log(obj); // { bar: 'bar' }
console.log(sym in obj); // true
console.log(obj[sym]); // foo
console.log(Object.keys(obj)); // ['bar']
얼핏 보면 symbol 을 사용 하여 대상 에 개인 속성 을 만 들 수 있 는 것 처럼 보 입 니 다.많은 다른 프로 그래 밍 언어 는 그 유형 에서 자신의 개인 속성 을 가지 고 있 습 니 다.개인 속성 누락 은 자바 script 의 단점 으로 여 겨 져 왔 습 니 다.
불 행 히 도 이 대상 과 상호작용 하 는 코드 는 키 가 symbol 인 속성 에 접근 할 수 있 습 니 다.호출 코드 가 symbol 자체 에 접근 할 수 없 는 상황 에서 가능 합 니 다.예 를 들 어 Reflect.ownKeys()방법 은 대상 의 모든 키 목록 을 가 져 올 수 있 습 니 다.문자열 과 symbol 을 포함 합 니 다.

function tryToAddPrivate(o) {
  o[Symbol("Pseudo Private")] = 42;
}
const obj = { prop: "hello" };
tryToAddPrivate(obj);
console.log(Reflect.ownKeys(obj));
// [ 'prop', Symbol(Pseudo Private) ]
console.log(obj[Reflect.ownKeys(obj)[1]]); // 42
메모:현재 자바 스 크 립 트 에서 클래스 에 개인 속성 을 추가 하 는 문 제 를 처리 하 는 작업 을 하고 있 습 니 다.이 특성의 이름 은 개인 필드 라 고 불 린 다.모든 대상 에 게 혜택 을 주지 않 지만 인 스 턴 스 대상 에 게 혜택 을 줄 수 있다.개인 필드 는 Chrome 74 부터 사용 할 수 있 습 니 다.
코드 배치 후 존재 할 수 있 는 BUG 는 실시 간 으로 알 수 없습니다.나중에 이러한 BUG 를 해결 하기 위해 많은 시간 을 들 여 log 디 버 깅 을 했 습 니 다.여기 서 좋 은 BUG 모니터링 도구 인 Fundebug 를 추천 합 니 다.
속성 이름 충돌 방지
기 호 는 자바 스 크 립 트 가 대상 에 게 개인 속성 을 제공 하 는 데 직접적인 도움 이 되 지 않 을 수 있 습 니 다.그러나 그들 은 유익 한 또 다른 원인 이다.서로 다른 라 이브 러 리 가 이름 충돌 위험 이 없 이 대상 에 속성 을 추가 하려 고 할 때 유용 합 니 다.
Symbol 은 자바 Scrit 대상 에 게 개인 속성 을 제공 하 는 것 은 어렵 지만 Symbol 은 다른 장점 이 있 습 니 다.서로 다른 라 이브 러 리 가 대상 에 게 속성 을 추가 할 때 이름 충돌 위험 이 존재 하지 않도록 하 는 것 입 니 다.
이러한 상황 을 고려 합 니 다.두 개의 서로 다른 라 이브 러 리 는 한 대상 에 게 기본 데 이 터 를 추가 하려 고 합 니 다.아마도 그들 은 모두 대상 에 어떤 식별 자 를 설정 하려 고 할 것 입 니 다.간단하게 id 를 키 로 사용 하면 커 다란 위험 이 존재 합 니 다.바로 여러 라 이브 러 리 가 같은 키 를 사용 할 것 입 니 다.

function lib1tag(obj) {
  obj.id = 42;
}
function lib2tag(obj) {
  obj.id = 369;
}
Symbol 을 사용 하면 라 이브 러 리 마다 필요 한 Symbol 을 생 성 할 수 있 습 니 다.그리고 Symbol 을 생 성 하 는 값 을 대상 으로 하 는 속성:

const library1property = Symbol("lib1");
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = Symbol("lib2");
function lib2tag(obj) {
  obj[library2property] = 369;
}
이 때문에 Symbol 은 자 바스 크 립 트 에 확실히 유리 한 것 같다.
그러나 라 이브 러 리 마다 왜 랜 덤 문자열 을 만 들 거나 네 임 스페이스 를 사용 하지 못 하 는 지 물 어 볼 수 있 습 니 다.

const library1property = uuid(); // random approach
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = "LIB2-NAMESPACE-id"; // namespaced approach
function lib2tag(obj) {
  obj[library2property] = 369;
}
이 방법 은 맞습니다.이 방법 은 실제로 Symbol 의 방법 과 매우 비슷 합 니 다.두 라 이브 러 리 가 같은 속성 명 을 사용 하지 않 는 한 충돌 할 위험 이 없습니다.
이런 점 에서 똑똑 한 독자 들 은 이 두 가지 방법 이 완전히 같 지 않다 고 지적 할 것 이다.우리 가 유일한 이름 의 속성 명 을 사용 하 는 것 은 여전히 단점 이 있다.그들의 키 는 매우 쉽게 찾 을 수 있다.특히 코드 를 실행 하여 키 를 교체 하거나 직렬 화 대상 을 찾 을 때.아래 의 예 를 고려 하 다.

const library2property = "LIB2-NAMESPACE-id"; // namespaced
function lib2tag(obj) {
  obj[library2property] = 369;
}
const user = {
  name: "Thomas Hunter II",
  age: 32
};
lib2tag(user);
JSON.stringify(user);
// '{"name":"Thomas Hunter II","age":32,"LIB2-NAMESPACE-id":369}'
만약 우리 가 대상 의 속성 명 에 Symbol 을 사용 했다 면 JSON 출력 은 그 값 을 포함 하지 않 을 것 입 니 다.왜 그 럴 까요?JavaScript 가 Symbol 에 대한 지 지 를 받 았 지만 JSON 규범 이 바 뀌 었 다 는 것 은 아 닙 니 다!JSON 은 문자열 만 키 로 허용 하고 자 바스 크 립 트 는 최종 JSON 유효 부하 에서 Symbol 속성 을 표시 하려 고 하지 않 습 니 다.

const library2property = "f468c902-26ed-4b2e-81d6-5775ae7eec5d"; // namespaced approach
function lib2tag(obj) {
  Object.defineProperty(obj, library2property, {
    enumerable: false,
    value: 369
  });
}
const user = {
  name: "Thomas Hunter II",
  age: 32
};
lib2tag(user);
console.log(user); // {name: "Thomas Hunter II", age: 32, f468c902-26ed-4b2e-81d6-5775ae7eec5d: 369}
console.log(JSON.stringify(user)); // {"name":"Thomas Hunter II","age":32}
console.log(user[library2property]); // 369
enumerable 속성 을 false 로 설정 하여'숨 김'문자열 키 를 사용 하 는 행 위 는 Symbol 키 와 매우 유사 합 니 다.Object.keys()를 통 해 옮 겨 다 녀 도 보이 지 않 지만 Reflect.ownKeys()를 통 해 다음 과 같은 예 시 를 볼 수 있 습 니 다.

const obj = {};
obj[Symbol()] = 1;
Object.defineProperty(obj, "foo", {
  enumberable: false,
  value: 2
});
console.log(Object.keys(obj)); // []
console.log(Reflect.ownKeys(obj)); // [ 'foo', Symbol() ]
console.log(JSON.stringify(obj)); // {}
그런 점 에서 우 리 는 거의 Symbol 을 다시 만 들 었 다.숨겨 진 문자열 속성 과 Symbol 은 모두 직렬 화 기 를 숨 깁 니 다.이 두 속성 은 모두 Reflect.ownKeys()방법 으로 읽 을 수 있 기 때문에 사실상 개인 적 인 것 이 아니다.속성 이름 의 문자열 버 전이 특정한 이름 공간/무 작위 값 을 사용한다 고 가정 하면 여러 라 이브 러 리 에서 예상 치 못 한 이름 충돌 위험 을 제거 합 니 다.
그러나 여전히 작은 차이 가 있다.문자열 은 가 변 적 이지 않 고 Symbol 은 항상 유일한 것 을 보장 하기 때문에 문자열 조합 을 생 성 하 는 데 충돌 이 생 길 수 있 습 니 다.수학 적 으로 는 Symbol 이 문자열 에서 얻 을 수 없 는 혜택 을 제공 한 다 는 뜻 이다.
Node.js 에서 대상 을 검사 할 때(예 를 들 어 console.log()를 사용 합 니 다.inspect 라 는 대상 의 방법 을 만나면 이 함 수 를 호출 하고 내용 을 인쇄 합 니 다.모든 사람 이 원 하 는 것 이 아니 라 inspect 라 고 부 르 는 방법 은 사용자 가 만 든 대상 과 자주 충돌 하 는 것 을 상상 할 수 있 습 니 다.
현재 Symbol 은 이 기능 을 실현 할 수 있 으 며 equire(util).inspect.custom 에서 사용 할 수 있 습 니 다.inspect 방법 은 Node.js v10 에서 폐기 되 었 고 v1 1 에서 완전히 무시 되 었 습 니 다.지금 은 검사 행 위 를 우연히 바 꾸 는 사람 이 없습니다.
아 날로 그 개인 속성
여기 에는 대상 의 개인 속성 을 모 의 하 는 재 미 있 는 방법 이 있다.이 방법 은 다른 자 바스 크 립 트 특성 인 proxy(에이전트)를 이용 합 니 다.대 리 는 본질 적 으로 대상 을 봉인 하고 우리 가 이 대상 과 의 각종 조작 에 관여 하도록 허락 했다.
대 리 는 대상 에서 실행 되 는 조작 을 차단 하기 위해 많은 방법 을 제공 했다.우 리 는 프 록 시 를 사용 하여 대상 에 사용 할 수 있 는 속성 을 설명 할 수 있 습 니 다.이 경우,우 리 는 이미 알 고 있 는 숨겨 진 속성 을 숨 기 는 프 록 시 를 만 들 것 입 니 다.하 나 는 문자열 입 니 다.favColor,다른 하 나 는 favBook 에 분 배 된 S ymbol 입 니 다.

let proxy;

{
  const favBook = Symbol("fav book");

  const obj = {
    name: "Thomas Hunter II",
    age: 32,
    _favColor: "blue",
    [favBook]: "Metro 2033",
    [Symbol("visible")]: "foo"
  };

  const handler = {
    ownKeys: target => {
      const reportedKeys = [];
      const actualKeys = Reflect.ownKeys(target);

      for (const key of actualKeys) {
        if (key === favBook || key === "_favColor") {
          continue;
        }
        reportedKeys.push(key);
      }

      return reportedKeys;
    }
  };

  proxy = new Proxy(obj, handler);
}

console.log(Object.keys(proxy)); // [ 'name', 'age' ]
console.log(Reflect.ownKeys(proxy)); // [ 'name', 'age', Symbol(visible) ]
console.log(Object.getOwnPropertyNames(proxy)); // [ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(proxy)); // [Symbol(visible)]
console.log(proxy._favColor); // 'blue'
사용fav Color 문자열 은 간단 합 니 다.라 이브 러 리 의 소스 코드 만 읽 으 면 됩 니 다.또한,강력 한 힘 을 통 해 동적 키 를 찾 습 니 다(예 를 들 어 앞의 uid 예제).단,Symbol 에 대한 직접적인 인용 이 없 으 면 누구 도 proxy 대상 에서'Metro 2033'값 을 방문 할 수 없습니다.
Node.js 경고:Node.js 에는 에이전트 의 프라이버시 를 파괴 하 는 기능 이 있 습 니 다.자 바스 크 립 트 언어 자체 에 이 기능 이 존재 하지 않 으 며 웹 브 라 우 저 와 같은 다른 경우 에는 적용 되 지 않 습 니 다.그것 은 주어진 에이전트 에서 하위 대상 에 대한 접근 권 을 얻 을 수 있 도록 합 니 다.다음은 이 기능 을 사용 하여 상술 한 사유 속성 예 시 를 깨 는 예시 입 니 다.

const [originalObject] = process.binding("util").getProxyDetails(proxy);
const allKeys = Reflect.ownKeys(originalObject);
console.log(allKeys[3]); // Symbol(fav book)
현재,우 리 는 전역 Reflect 대상 을 수정 하거나 util 프로 세 스 바 인 딩 을 수정 하여 특정한 Node.js 인 스 턴 스 에서 사용 하지 않도록 해 야 합 니 다.
위 에서 말 한 것 은 편집장 이 여러분 에 게 소개 한 자 바스 크 립 트 가 왜 Symbol 유형 이 있 는 지 상세 하 게 통합 되 어야 하 는 지 여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.편집장 은 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기