[javascript] JavaScript ES6 클래스의 전용 속성

ES6 클래스에서 개인 속성을 만들 수 있습니까?

다음은 예입니다. 에 대한 액세스를 방지하려면 어떻게해야 instance.property합니까?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"


답변

프라이빗 필드 (및 메소드)는 ECMA 표준 에서 구현되고 있습니다 . babel 7 및 stage 3 프리셋으로 오늘 사용을 시작할 수 있습니다 .

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }

  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return this.#privateMethod();
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> hello world

답변

짧은 대답은 아닙니다. ES6 클래스를 사용하는 개인 속성에 대한 기본 지원은 없습니다.

그러나 새 속성을 객체에 첨부하지 않고 클래스 생성자 내에 유지하고 getter 및 setter를 사용하여 숨겨진 속성에 도달하면 해당 동작을 모방 할 수 있습니다. 게터와 세터는 클래스의 새로운 인스턴스마다 재정의됩니다.

ES6

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}

답변

@loganfsmyth의 답변을 확장하려면 :

JavaScript의 유일한 개인 데이터는 여전히 범위 변수입니다. 공개 속성과 같은 방식으로 내부적으로 액세스되는 속성의 의미에서 개인 속성을 가질 수는 없지만 범위 변수를 사용하여 개인 데이터를 저장할 수 있습니다.

범위가 지정된 변수

여기서 접근 방식은 개인용 생성자 함수의 범위를 사용하여 개인용 데이터를 저장하는 것입니다. 이 개인 데이터에 액세스 할 수있는 메소드의 경우 생성자 내에서도 작성해야합니다. 즉, 모든 인스턴스에서 메소드를 다시 작성해야합니다. 이것은 성능 및 메모리 페널티이지만 일부는 페널티가 허용된다고 생각합니다. 개인 데이터에 접근 할 필요가없는 메소드는 평소와 같이 프로토 타입에 추가하여 패널티를 피할 수 있습니다.

예:

function Person(name) {
  let age = 20; // this is private
  this.name = name; // this is public

  this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
  };
}

let joe = new Person('Joe');
joe.greet();

// here we can access name but not age

범위가 약한지도

WeakMap은 이전 접근 방식의 성능과 메모리 페널티를 피하기 위해 사용할 수 있습니다. WeakMaps는 해당 WeakMap을 통해서만 액세스 할 수있는 방식으로 데이터를 Objects (여기서는 인스턴스)와 연결합니다. 따라서 범위 변수 방법을 사용하여 개인 WeakMap을 만든 다음 해당 WeakMap을 사용하여 관련 개인 데이터를 검색합니다.this 합니다. 모든 인스턴스가 단일 WeakMap을 공유 할 수 있기 때문에 범위 변수 방법보다 빠릅니다. 따라서 메소드를 고유 한 WeakMap에 액세스하기 위해 메소드를 다시 작성할 필요가 없습니다.

예:

let Person = (function () {
  let privateProps = new WeakMap();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      privateProps.set(this, {age: 20}); // this is private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// here we can access joe's name but not age

이 예제는 Object를 사용하여 여러 개인 속성에 하나의 WeakMap을 사용합니다. 여러 WeakMaps를 사용하여처럼 사용 age.set(this, 20)하거나 작은 래퍼를 작성하여 다른 방법으로 사용할 수도 있습니다.privateProps.set(this, 'age', 0) 있습니다.

이 접근법의 프라이버시는 전 세계를 무단 변경함으로써 이론적으로 위반 될 수 있습니다 WeakMap 개체를 . 즉, 모든 JavaScript는 엉망이 된 전역에서 깨질 수 있습니다. 우리의 코드는 이미 이것이 일어나지 않는다는 가정에 기반하고 있습니다.

(이 방법은로도 수행 할 수 Map있지만 매우주의하지 않으면 메모리 누수가 발생 WeakMap하기 때문에 더 좋습니다. Map이 목적을 위해 두 가지가 다르지 않습니다.)

반답 : 범위가 지정된 기호

기호는 속성 이름으로 사용할 수있는 기본 값 유형입니다. 범위가 지정된 변수 방법을 사용하여 개인 심볼을 만든 다음 개인 데이터를this[mySymbol] 있습니다.

이 방법의 개인 정보는 Object.getOwnPropertySymbols 있지만 다소 어색합니다.

예:

let Person = (function () {
  let ageKey = Symbol();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      this[ageKey] = 20; // this is intended to be private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${this[ageKey]}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// Here we can access joe's name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.

반답 : 밑줄

이전 기본값은 밑줄이 붙은 공용 속성을 사용하는 것입니다. 어떤 식 으로든 사유 재산이 아니지만,이 협약은 독자들이 그 사유를 사유로 취급해야한다는 의사 소통을 잘 수행 할 수있을 정도로 널리 퍼져 있습니다. 이 시간이 지남에 따라 우리는 읽기 쉽고 타이핑하기 쉽고 빠른 접근 방식을 얻습니다.

예:

class Person {
  constructor(name) {
    this.name = name; // this is public
    this._age = 20; // this is intended to be private
  }

  greet() {
    // Here we can access both name and age
    console.log(`name: ${this.name}, age: ${this._age}`);
  }
}

let joe = new Person('Joe');
joe.greet();

// Here we can access both joe's name and age. But we know we aren't
// supposed to access his age, which just might stop us.

결론

ES2017 기준으로 여전히 개인 재산을 수행하는 완벽한 방법은 없습니다. 다양한 접근 방식에는 장단점이 있습니다. 범위가 지정된 변수는 실제로 비공개입니다. 범위가 지정된 WeakMap은 범위가 지정된 변수보다 매우 개인적이며 더 실용적입니다. 범위가 지정된 기호는 합리적으로 사적이며 합리적입니다. 밑줄은 종종 충분히 사적이며 매우 실용적입니다.


답변

업데이트 : 더 좋은 구문제안 이 진행 중입니다. 기부를 환영합니다.


예, 객체에 범위가 지정된 액세스가 있습니다 . ES6에 Symbols가 도입되었습니다 .

심볼은 고유하며 리플렉션 (Java / C #의 개인용)을 제외하고는 외부에서 액세스 할 수 없지만 내부의 심볼에 액세스 할 수있는 사용자는 키 액세스에 사용할 수 있습니다.

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, can only access with access to the Symbol

답변

대답은 ‘아니오”. 그러나 다음과 같은 속성에 대한 개인 액세스 권한을 만들 수 있습니다.

ES6 사양의 이전 버전에서는 프라이버시를 보장하기 위해 Symbols를 사용할 수 있다는 제안은 더 이상 사실이 아닙니다 : https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604 HTMLhttps://stackoverflow.com/a/22280202/1282216 기호 및 개인 정보 보호에 대한 더 긴 설명은 다음을 참조하십시오. https://curiosity-driven.org/private-properties-in-javascript )


답변

JS에서 진정한 프라이버시를 얻는 유일한 방법은 범위를 지정 this하는 것이므로 구성 요소 내에서만 액세스 할 수 있는 멤버 인 특성을 가질 수있는 방법이 없습니다 . ES6에 개인 정보를 저장하는 가장 좋은 방법은 WeakMap을 사용하는 것입니다.

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

분명히 이것은 아마도 느리고 확실히 추악하지만 개인 정보를 제공합니다.

Javascript가 매우 동적이기 때문에 이조 차도 완벽하지는 않습니다. 누군가는 여전히 할 수 있습니다

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

그들이가 저장되는대로 추가 조심하기를 원한다면 캐치 값으로, 그래서 당신의 로컬 참조를 캡처해야 할 것입니다 .set.get재정의 프로토 타입에 의존 명시 적으로 대신 사용할 수 있습니다.

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}

답변

나중에 다른 조회자를 참조 하기 위해 WeakMaps 를 사용 하여 개인 데이터를 보유 하는 것이 좋습니다 .

보다 명확하고 실제적인 예는 다음과 같습니다.

function storePrivateProperties(a, b, c, d) {
  let privateData = new WeakMap;
  // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
  let keyA = {}, keyB = {}, keyC = {}, keyD = {};

  privateData.set(keyA, a);
  privateData.set(keyB, b);
  privateData.set(keyC, c);
  privateData.set(keyD, d);

  return {
    logPrivateKey(key) {
      switch(key) {
      case "a":
        console.log(privateData.get(keyA));
        break;
      case "b":
        console.log(privateData.get(keyB));
        break;
      case "c":
        console.log(privateData.get(keyC));
        break;
      case "d":
        console.log(privateData.set(keyD));
        break;
      default:
        console.log(`There is no value for ${key}`)
      }
    }
  }
}