JavaScript ES6 클래스의 전용 속성


444

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

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

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

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

5
실제로이 기능에 대한 3 단계 제안이 있습니다 -tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty

@arty 나는 이에 대한 답변을 예제로 제공했다 : stackoverflow.com/a/52237988/1432509
Alister

답변:


165

프라이빗 필드 (및 메소드)는 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

그 클래스 필드가 어떻게 작동하는지 궁금합니다. 현재 this전화하기 전에 생성자에서 사용할 수 없습니다 super(). 그러나 babel은 그것들을 슈퍼보다 먼저 넣습니다.
seeker_of_bacon

#privateCrap구문 을 허용하도록 ESLint를 구성하는 방법은 무엇입니까?
Marecky

6
그리고 eslint는 어떻습니까? 등호에 파서 오류가 있습니다. Babel이 작동 중입니다. eslint는이 새로운 js 구문을 구문 분석 할 수 없습니다.
martonx 2019

6
와우 이건 정말 못 생겼어 해시 태그는 유효한 문자입니다. 그 부동산은 실제로 사적이지 않습니까? .. TypeScript에서 확인했습니다. 비공개 멤버는 비공개 또는 읽기 전용 (외부에서)으로 컴파일되지 않습니다. 다른 (공용) 재산처럼 선언되었습니다. (ES5).
도미니크

2
이것으로 어떻게 개인 메소드 를 작성 합니까? 내가 이것을 할 수 있을까 : #beep() {}; 그리고 이것 : async #bzzzt() {}?
Константин Ван 2016 년

277

짧은 대답은 아닙니다. 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; }
}

1
이 솔루션이 가장 좋습니다. 나는 그것이 스케일링에 사용되어서는 안된다는 것에 동의하지만 일반적으로 포함 당 한 번만 인스턴스화되는 클래스에 적합합니다.
Blake Regalia

2
또한 새 클래스를 만들 때마다이 클래스의 모든 단일 구성 요소를 재정의합니다.
Quentin Roy

10
너무 이상하다! ES6에서는 ES6 이전보다 더 많은 "클로저 피라미드"를 만들고 있습니다! 생성자 내에서 함수를 정의하면 위의 ES5 예제에서보다 더보기 흉하게 보입니다.
Kokodoko

1
OP는 ES6 클래스에 대해 구체적으로 요구하기 때문에 개인적으로 이것이 기술적으로 작동하더라도 좋지 않은 해결책이라고 생각합니다. 주요 제한 사항은 이제 개인 변수를 사용하는 모든 클래스 메서드가 생성자 내부에서 선언되어야하므로 class구문이 처음부터 장점을 크게 손상 시킨다는 것입니다 .
NanoWizard

10
이 모든 것은 간접적 인 도입입니다. 이제 getNamesetName속성을 어떻게 비공개로 설정합니까?
aij

195

@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은 범위가 지정된 변수보다 매우 개인적이며 더 실용적입니다. 범위가 지정된 기호는 합리적으로 사적이며 합리적입니다. 밑줄은 종종 충분히 사적이며 매우 실용적입니다.


7
첫 번째 예제 스 니펫 ( "범위 변수")은 전체 반 패턴입니다. 반환 된 각 객체는 다른 클래스를 갖습니다. 하지마 권한있는 메소드를 원하면 생성자에서 작성하십시오.
Bergi

1
클래스를 함수 안에 넣는 것은 처음에 클래스를 사용하는 전체 목적을 어기는 것 같습니다. 이미 함수를 사용하여 인스턴스를 작성하는 경우 모든 비공개 / 공개 멤버를 해당 함수 내에 배치하고 전체 클래스 키워드를 잊어 버릴 수도 있습니다.
Kokodoko

2
@Bergi @Kokodoko 범위 변수 접근 방식이 약간 더 빨라지고 깨지지 않도록 편집했습니다 instanceof. 나는 그 접근법이 완전성을 위해서만 포함 된 것으로 생각하고 있으며 그것이 실제로 얼마나 능력이 있는지에 대해 더 많은 생각을했을 것입니다.
tristan

1
훌륭한 설명! ES6가 실제로 개인 변수를 시뮬레이션하기가 더 어려워 졌다는 것에 놀랐습니다. ES5에서는 var를 사용하여 개인 및 공개를 시뮬레이션 할 수 있습니다.
코코 도코

2
@Kokodoko 클래스를 사용하지 않고 모든 것을 함수에 넣으면 프로토 타입 메서드를 사용하여 상속 구현으로 되돌려 야합니다. 클래스에서 확장을 사용하는 것이 훨씬 깔끔한 접근 방식이므로 함수 내부에서 클래스를 사용하는 것은 완전히 허용됩니다.
AndroidDev

117

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


예, 객체에 범위가 지정된 액세스가 있습니다 . 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

6
사용할 수 없습니까 Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy

41
@ BenjaminGruenbaum : 분명히 상징은 더 이상 진정한 개인 정보를 보장하지 않습니다 : stackoverflow.com/a/22280202/1282216
d13

28
세 키를 통해 @trusktr? 아닙니다. 상징을 통해? 예. C # 및 Java와 같은 언어에서 리플렉션을 사용하여 개인 필드에 액세스하는 방법과 매우 유사합니다. 액세스 수정자는 보안에 관한 것이 아니라 의도의 명확성에 관한 것입니다.
Benjamin Gruenbaum

9
Symbols를 사용하는 const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();것은. 변수를 캡슐화하기 위해 클로저를 사용하는 것을 의미하는 "비공개"JavaScript를 고려할 것입니다. 따라서 이러한 변수는 리플렉션을 통해 액세스 할 수 없습니다.
trusktr

13
또한 privateand protected키워드 를 사용하는 것이 Symbolor 보다 훨씬 더 깨끗 하다고 생각합니다 Name. 대괄호 표기법보다는 점 표기법을 선호합니다. 나는 사적인 것들에 점을 계속 사용하고 싶습니다. this.privateVar
trusktr

33

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

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 )


6
-1, 이것은 귀하의 질문에 실제로 대답하지 않습니다. ES5에서도 IIFE와 함께 클로저를 사용할 수 있습니다. 개인 속성은 대부분의 언어 (Java, C # 등)에서 리플렉션을 통해 열거 할 수 있습니다. 사유 재산의 요점은 다른 집주인에게 의도를 전달하고 보안을 강화하지 않는 것입니다.
Benjamin Gruenbaum

1
@ BenjaminGruenbaum, 나는 더 나은 대답을 얻었 으면 좋겠다.
d13

프로그래밍 환경에서 액세스 할 수없는 멤버를 달성하는 데 여전히 기호가 유효한 방법이라고 생각합니다. 네, reallllyyy 원한다면 여전히 찾을 수 있지만 그게 요점이 아닙니다. 민감한 정보를 저장해서는 안되지만 클라이언트 측 코드에서는 그렇게해서는 안됩니다. 그러나 외부 클래스에서 속성이나 메서드를 숨기려는 목적으로 작동합니다.
Kokodoko

클래스의 개인 속성 대신 모듈 수준에서 범위가 지정된 변수를 사용하면 statitc 속성과 비슷한 singleton.behavior 또는 behavior가 발생합니다 .vars 인스턴스는 공유됩니다.
Adrian Moisa

30

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));
  }
}

3
제안으로 객체를 값으로 사용하여 속성 당 하나의 약한 맵을 사용하지 않아도됩니다. 이 방법으로지도의 get수를 방법 당 하나로 줄일 수도 있습니다 (예 :) const _ = privates.get(this); console.log(_.privateProp1);.
Quentin Roy

예, 그것은 완전히 옵션입니다. 실제 속성을 사용할 때 사용자가 작성한 것에 더 직접 매핑되기 때문에 주로 이것을 사용했습니다.
loganfsmyth

@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"는 귀하의 재산이 비공개인지 아닌지 를 의미합니까?
Barbu Barbu

2
이것이 작동하려면 속성에 액세스하는 코드가 WeakMap 객체에 액세스해야합니다. WeakMap 객체는 일반적으로 모듈 내에서 범위가 지정되고 액세스 할 수 없습니다
loganfsmyth

22

나중에 다른 조회자를 참조 하기 위해 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}`)
      }
    }
  }
}

20
이러한 속성은 정적입니다.
Michael Theriot

8
나는 당신을 downvote하지 않았지만 당신의 weakmap 예제는 완전히 잘못되었습니다.
Benjamin Gruenbaum

4
즉, 인스턴스가 아닌 모든 클래스 인스턴스간에 데이터를 공유하고 있습니다. 적어도 고칠 수 있습니까?
Benjamin Gruenbaum

1
실제로, weakmap은 주어진 인스턴스에 첨부되어야합니다. 예제는 fitzgeraldnick.com/weblog/53 을 참조하십시오 .
3

2
MDN에 따르면 Symbols와 같은 기본 데이터 형식은 WeakMap 키로 사용할 수 없습니다. MDN WeakMap 문서
leepowell

12

에 따라 다름 물어 누구 :-)

어떤 private속성 수정은 포함되지 않습니다 극대 최소한의 클래스 제안 에 그것을 만든 것 같습니다 현재 초안 .

그러나 private 속성을 허용하는 private 이름지원 될 수 있으며 클래스 정의에도 사용될 수 있습니다.


3
그것은이다 매우 ES7에 대한 민간 것은 어떤 형태의 그들은 비록있는 거 생각 ES6에 개인 이름을 만들 것 같지는.
Qantas 94 Heavy

@ Qantas94 개인 이름과 고유 한 문자열 값이 모두 Symbols에서 내가 이해 한 것으로 대체되었습니다.
Benjamin Gruenbaum

네, 아마도 Symbols가 될 것입니다. 그러나 현재 사양에 포함되어있는 "기호"에 대한 설명은 [[prototype]]과 같은 내부 속성을 설명하는 데만 사용되며 사용자 코드에서 이러한 속성을 만들고 사용할 수있는 방법이 없습니다. 몇 가지 문서를 알고 있습니까?
Bergi

방금 모듈을 사용하여 개인 정보를 설정할 수 있다는 것을 깨달았습니다. 필요할 수있는 기호와 결합하면 ...?
d13

1
@Cody : 전체 모듈 코드는 ES6에서 자체 범위를 가지므로 IEFE가 필요하지 않습니다. 그리고 그렇습니다. 기호는 프라이버시가 아닌 고유성 (충돌 방지)을위한 것입니다.
Bergi

10

ES6 모듈 (@ d13에서 처음 제안)을 사용하면 나에게 잘 작동합니다. 개인 속성을 완벽하게 모방하지는 않지만 최소한 개인 속성이 클래스 외부로 누출되지 않을 것이라고 확신 할 수 있습니다. 예를 들면 다음과 같습니다.

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

그런 다음 소비 코드는 다음과 같습니다.

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

업데이트 (중요) :

주석에 @DanyalAytekin이 설명했듯이 이러한 개인 속성은 정적이므로 전체 범위입니다. 싱글 톤으로 작업 할 때는 잘 작동하지만 과도 객체에는주의를 기울여야합니다. 위의 예를 확장 :

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c

4
에 좋습니다 private static.
Danyal Aytekin

@DanyalAytekin : 아주 좋은 지적입니다. 이러한 개인 속성은 정적이므로 전역 적으로 유효합니다. 이것을 반영하여 답변을 업데이트했습니다.
Johnny Oshika

함수형 프로그래밍 (특히 Elm과 Haskell)에 대해 더 많이 배울수록 JS 프로그래머는 OOP 클래스 기반이 아닌 모듈 형 방식에 대한 모듈 기반 접근 방식의 이점을 얻을 수 있다고 생각합니다. ES6 모듈을 응용 프로그램 구축의 기초로 생각하고 클래스를 완전히 잊어 버린다면 전체적으로 훨씬 더 나은 응용 프로그램이 될 수 있다고 생각합니다. 숙련 된 Elm 또는 Haskell 사용자가이 접근 방식에 대해 언급 할 수 있습니까?
d13

1
업데이트에서 두 번째 a.say(); // a는 다음과 같아야합니다.b.say(); // b
grokky

let _message = null호출 생성자를 여러 번 할 때 너무 시원하지 않은 방법으로 시도 하면 엉망이됩니다.
Littlee

9

@ d13 및 @ johnny-oshika 및 @DanyalAytekin의 의견 작성 :

@ johnny-oshika가 제공 한 예제에서 화살표 함수 대신 일반 함수를 사용할 수 .bind있으며 현재 객체와 _privates객체를 카레 매개 변수로 사용할 수 있습니다.

something.js

function _greet(_privates) {
  return 'Hello ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sunny day!');

const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();

console.log(message1 === 'Hello Sunny day!');  // true
console.log(message2 === 'Hello Cloudy day!');  // true

// the followings are not public
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');

const message3 = something2.say();

console.log(message3 === 'Hello another Sunny day!'); // true

내가 생각할 수있는 이점 :

  • 우리는 개인 메소드를 가질 수 있습니다 ( _greet그리고 _updateMessage우리가 export참조 하지 않는 한 개인 메소드처럼 행동 합니다)
  • 그것들이 프로토 타입에 있지는 않지만, 위에서 언급 한 메소드는 인스턴스가 클래스 외부에서 한 번 생성되기 때문에 메모리를 절약 할 수 있습니다 (생성자에서 인스턴스를 정의하는 대신)
  • 우리는 모듈 내부에 있으므로 글로벌을 유출하지 않습니다
  • 바인딩 된 _privates객체를 사용하여 개인 속성을 가질 수도 있습니다

내가 생각할 수있는 몇 가지 단점 :

실행중인 스 니펫은 다음에서 찾을 수 있습니다. http://www.webpackbin.com/NJgI5J8lZ


8

예-캡슐화 된 속성을 만들 수 있습니다 있지만 ES6이 아닌 액세스 수정 자 (public | private)로는 수행되지 않았습니다.

다음은 ES6로 수행 할 수있는 간단한 예입니다.

1 클래스를 사용하여 클래스 만들기 단어를

2 내부에는 let OR const를 사용하여 생성자가 블록 범위 변수를 선언합니다. 예약어를 .-> 블록 범위이므로 외부에서 캡슐화 할 수 없습니다.

3 몇 가지 액세스 제어를 허용하기 | 사용 그것의 생성자 내에서 인스턴스 메소드를 선언 할 수있는 그 변수에 (세터 게터를) this.methodName=function(){}구문을

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

이제 확인하자 :

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value

1
이것은 생성자에서 선언 된 모든 메소드가 클래스의 각 인스턴스에 대해 다시 선언된다는 사실에도 불구하고 현재로서는이 문제에 대한 유일한 솔루션입니다. 이것은 성능 및 메모리 사용과 관련하여 매우 나쁜 생각입니다. 클래스 메소드는 생성자 범위 밖에서 선언해야합니다.
Freezystem

@Freezystem 우선 : 먼저 인스턴스 메소드입니다 (클래스 메소드 아님). 두 번째 OP 질문은 다음과 같습니다. _ instance.property에 대한 액세스를 어떻게 방지 할 수 있습니까? _ 내 대답은 다음과 같습니다. 방법의 예 ... 세 번째로 더 좋은 아이디어가 있다면 들으십시오
Nikita Kurtin

1
나는 당신이 틀렸다고 말하지 않고, new Something();당신의 메소드가 생성자에서 선언되어 있기 때문에 호출 할 때마다 각 인스턴스 메소드의 사본이 생성된다는 사실에도 불구하고 귀하의 솔루션은 개인 변수를 달성하는 가장 좋은 타협이라고 말했습니다. 개인 변수. 클래스의 인스턴스를 많이 만들면 많은 메모리 소비가 발생할 수 있으므로 성능 문제가 발생합니다. 메소드는 생성자 범위 밖에서 선언되어야합니다. 내 의견은 비판보다 솔루션 단점에 대한 설명이었습니다.
Freezystem

1
그러나 생성자 내부에서 전체 클래스를 정의하는 것은 나쁜 습관이 아닙니까? 우리는 지금 자바 스크립트를 "해킹"하지 않습니까? 다른 OOP 프로그래밍 언어를 살펴보면 생성자가 클래스를 정의하지 않는다는 것을 알 수 있습니다.
코코 도코

1
예, 이것이 제가 의미하는 바이며 귀하의 솔루션은 효과가 있습니다! 나는 ES6에 'class'키워드를 추가했지만 캡슐화를 달성하기 위해 var와 이것으로 작업하는 우아한 솔루션을 제거했다는 사실에 놀랐습니다.
코코 도코

8

"개인"에 대한 다른 접근법

ES6에서는 개인 가시성을 현재 사용할 수 없다는 사실과 싸우는 대신 IDE가 JSDoc (예 : Webstorm)을 지원하는 경우에는 더 실용적인 접근 방식을 사용하기로 결정했습니다. 아이디어는 @private태그 를 사용하는 것 입니다. 개발이 진행되는 한 IDE는 클래스 외부에서 개인 멤버에 액세스하지 못하게합니다. 나를 위해 잘 작동하고 내부 메서드를 숨기는 데 실제로 유용하므로 자동 완성 기능은 클래스가 실제로 노출하려고하는 것을 보여줍니다. 예를 들면 다음과 같습니다.

공개 자료 만 자동 완성


1
문제는 편집기를 통해 개인 변수에 액세스하지 않고 개인 변수를 외부에서 보호하지 않는다는 것입니다. 즉, 공개 / 개인이하는 것입니다. 코드가 완성되면 클래스 외부에서 이러한 변수에 액세스 할 수 있습니다 (그리고 중요한 생각 : override ). 귀하의 @private의견은 이러한 것들을 막을 수 없으며 문서 생성을위한 기능 일 뿐이며 IDE입니다.
Adrian Preuss

네, 알고 있습니다. 그것은 저에게 충분하고 다른 사람들에게 충분할 수도 있습니다. 나는 그것이 실제로 변수를 비공개로 만드는 것이 아니라는 것을 안다. 외부에서 액세스하지 말 것을 경고합니다 (물론 팀과 내가이 기능을 지원하는 IDE를 사용하는 경우에만). Javascript (및 Python과 같은 다른 언어)는 액세스 수준을 고려하여 설계되지 않았습니다. 사람들은 어떻게 든 그 기능을 구현하기 위해 모든 종류의 일을하지만 결국 우리는 언어를 해킹하여 결국 그 기능을 수행합니다. 나는 당신이 원한다면 좀 더 "자연적인"접근 방식으로 가기로 결정했습니다.
Lucio Paiva

6

약한지도

  • IE11에서 지원됨 (기호가 아님)
  • 하드 개인 (심볼을 사용하는 소품은 소프트 개인 Object.getOwnPropertySymbols)
  • 생성자에 모든 소품과 메소드가 필요한 클로저와 달리 정말 깨끗하게 보일 수 있습니다.

먼저 WeakMap을 감싸는 함수를 정의하십시오.

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

그런 다음 클래스 외부에 참조를 작성하십시오.

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // it's easy to set a private variable
  }

  getAge() {
    return p(this).age; // and get a private variable
  }
}

참고 : 클래스 는 IE11에서 지원되지 않지만 예제에서는 더 깔끔해 보입니다.


6

아, 너무 많은 이국적인 솔루션! 나는 보통 프라이버시를 신경 쓰지 않기 때문에 여기에 언급 된 것처럼 "의사 프라이버시" 를 사용합니다 . 그러나주의를 기울이면 (특별한 요구 사항이있는 경우)이 예제와 같은 것을 사용합니다.

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

가능한 또 다른 기능 구현 (생성자) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}

5

개인적으로 나는 bind 연산자 의 제안을 좋아하고 ::언급 한 @ d13 솔루션과 결합하지만 지금 export은 클래스에 키워드를 사용 하고 모듈에 개인 함수를 넣는 @ d13의 대답을 고수 합니다.

여기에 언급되지 않은 또 하나의 해결책이 있습니다. 다음은 더 기능적인 접근 방식이며 클래스 내 모든 개인 소품 / 메소드를 가질 수 있습니다.

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

그것에 대한 의견을 부탁드립니다.


일반적으로 접근 방식이 마음에 듭니다. 피드백 : 1. 충돌을 방지하기 위해 각 클래스마다 다른 private.js 모듈이 필요합니다. 2. 나는 각각의 개인 메소드를 인라인으로 정의함으로써 생성자를 정말로 길게 만드는 잠재력을 싫어한다. 3. 모든 클래스 메소드가 하나의 파일에 있으면 좋을 것입니다.
Doug Coburn

5

"클래스 개인 데이터"에 대한 모범 사례를 찾을 때이 게시물을 보았습니다. 일부 패턴에는 성능 문제가있을 수 있다고 언급했습니다.

온라인 책 "Exploring ES6"의 4 가지 주요 패턴을 기반으로 몇 가지 jsperf 테스트를 구성했습니다.

http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

테스트는 여기에서 찾을 수 있습니다.

https://jsperf.com/private-data-for-classes

Chrome 63.0.3239 / Mac OS X 10.11.6에서 가장 실적이 좋은 패턴은 "생성자 환경을 통한 개인 데이터"및 "이름 지정 규칙을 통한 개인 데이터"였습니다. 나에게 Safari는 WeakMap에서 잘 수행했지만 Chrome은 잘 수행하지 못했습니다.

메모리에 미치는 영향을 모르지만 성능 구성 문제라고 경고 한 "구축 자 환경"의 패턴은 매우 성능이 좋았습니다.

4 가지 기본 패턴은 다음과 같습니다.

생성자 환경을 통한 개인 데이터

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

생성자 환경을 통한 개인 데이터 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

명명 규칙을 통한 개인 데이터

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

WeakMaps를 통한 개인 데이터

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

심볼을 통한 개인 데이터

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

4

나는 생성자 내부의 클로저를 사용하여 '두 세계의 최고'를 얻는 것이 가능하다고 생각합니다. 두 가지 변형이 있습니다.

모든 데이터 멤버는 비공개입니다

function myFunc() {
   console.log('Value of x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Enhanced value of x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(internal);
      
      this.myFunc = myFunc.bind(internal);
   }
};

일부 회원은 비공개입니다

참고 : 이것은 분명히 추한 것입니다. 더 나은 해결책을 알고 있다면이 응답을 편집하십시오.

function myFunc(priv, pub) {
   pub.y = 3; // The Test object now gets a member 'y' with value 3.
   console.log('Value of x: ' + priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // The Test object now gets a member 'z' with value 3.
   console.log('Enhanced value of x: ' + (priv.x + 1));
}

class Test {
   constructor() {
      
      let self = this;

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);
      
      this.myFunc = myFunc.bind(null, internal, self);
   }
};


4

실제로 기호 및 프록시를 사용하는 것이 가능합니다. 클래스 범위에서 심볼을 사용하고 프록시에서 두 개의 트랩을 설정합니다. 하나는 클래스 프로토 타입 용으로 Reflect.ownKeys (instance) 또는 Object.getOwnPropertySymbols는 심볼을 제공하지 않고 다른 하나는 생성자 자체를위한 것입니다. 따라서 new ClassName(attrs)호출되면 반환 된 인스턴스가 인터셉트되고 자체 속성 기호가 차단됩니다. 코드는 다음과 같습니다.

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // public
    this[pet] = 'dog'; // private 
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // private
      return 'Hi there!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` The pet is a ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']


// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

Reflect.ownKeys()그렇게 작동 Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))합니다. 그래서 우리는 이러한 객체에 대한 트랩이 필요합니다.


고마워, 나는 상징을 시험해 볼 것이다 :) 위의 모든 답변에서 접근 할 수없는 클래스 멤버를 만드는 가장 간단한 방법 인 것 같습니다 :)
Kokodoko

4

Typescript조차도 할 수 없습니다. 그들의 문서에서 :

멤버가 개인으로 표시되면 포함 클래스 외부에서 액세스 할 수 없습니다. 예를 들면 다음과 같습니다.

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

그러나 놀이터 에서 번역 된 결과 는 다음과 같습니다.

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

따라서 "비공개"키워드는 효과가 없습니다.


2
IDE에있는 동안 "나쁜"프로그래밍을 막기 때문에 여전히 효과적입니다. 사용하지 말아야 할 구성원을 보여줍니다. 그것이 개인 및 공공을 사용하는 주된 이유라고 생각합니다. (예를 들어, C #을 기계어 코드로 컴파일 할 때 private은 여전히 ​​private입니까? 누가 알겠습니까?). 다른 답변을 읽을 때 @Symbol을 사용하면 멤버에 액세스 할 수 없게 될 수 있습니다. 그러나 콘솔에서도 Symbols를 찾을 수 있습니다.
Kokodoko

TypeScript를 JavaScript로 변환하는 동안 TypeScript 오류가 발생합니까? (유형 검사는 transpite 시간에 발생합니다. 일부 런타임 개인 메커니즘이
아닙니다

4

이 파티에 매우 늦었지만 검색에서 OP 질문에 부딪 쳤습니다. 예, 클래스 선언을 클로저로 묶어 개인 속성을 가질 수 있습니다

이 코드 펜에 개인 메소드를 사용 하는 방법에 대한 예가 있습니다. 아래 스 니펫에서 Subscribable 클래스에는 두 개의 '비공개'기능 process과가 processCallbacks있습니다. 이러한 방식으로 모든 속성을 추가 할 수 있으며 클로저를 사용하여 비공개로 유지됩니다. 관심사가 잘 분리되어 있고 클로저가 깔끔하게 작업을 수행 할 때 구문을 추가하여 Javascript를 부 풀릴 필요가없는 경우 IMO 프라이버시는 거의 필요하지 않습니다.

const Subscribable = (function(){

  const process = (self, eventName, args) => {
    self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};

  const processCallbacks = (self, eventName, args) => {
    if (self.callingBack.get(eventName).length > 0){
      const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
      self.callingBack.set(eventName, callingBack);
      process(self, eventName, args);
      nextCallback(...args)}
    else {
      delete self.processing.delete(eventName)}};

  return class {
    constructor(){
      this.callingBack = new Map();
      this.processing = new Map();
      this.toCallbacks = new Map()}

    subscribe(eventName, callback){
      const callbacks = this.unsubscribe(eventName, callback);
      this.toCallbacks.set(eventName,  [...callbacks, callback]);
      return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience

    unsubscribe(eventName, callback){
      let callbacks = this.toCallbacks.get(eventName) || [];
      callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
      if (callbacks.length > 0) {
        this.toCallbacks.set(eventName, callbacks)}
      else {
        this.toCallbacks.delete(eventName)}
      return callbacks}

    emit(eventName, ...args){
      this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
      if (!this.processing.has(eventName)){
        process(this, eventName, args)}}}})();

이 접근 방식은 관심사를 멋지게 분리하고 사물을 진실로 유지하기 때문에 좋아합니다. 유일한 단점은 개인 콘텐츠에서 'this'를 참조하기 위해 'self'(또는 이와 유사한 것)를 사용해야한다는 것입니다.


4

벤자민의 대답 은 아마도 언어가 명시 적으로 개인 변수를 지원할 때까지 대부분의 경우에 가장 적합 하다고 생각 합니다.

그러나 어떤 이유로으로 액세스를 방지 해야하는 경우 Object.getOwnPropertySymbols()사용을 고려한 방법은 구성 할 수없는 고유하고 구성 가능하며 열거 할 수 없으며 쓸 수없는 속성을 구성하는 각 객체에 속성 식별자로 사용할 수 있습니다 (예 :와 같은 Symbol다른 고유 속성이없는 경우 unique 등 id). 그런 다음 해당 식별자를 사용하여 각 객체의 '비공개'변수 맵을 유지하십시오.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "I'm hidden"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "I'm public";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // A clean up method of some kind is necessary since the
    // variables won't be cleaned up from memory automatically
    // when the object is garbage collected
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

성능을 고려할 경우이 방법을 사용하는 WeakMap보다 잠재적 인 이점은 액세스 시간더 빠릅니다 .


1
내가 틀렸다면 수정하지만 privateVars는 객체가 이미 파괴 된 경우에도 객체의 개인 변수를 계속 저장하기 때문에이 코드에는 메모리 누수가 포함되어 있지 않습니까?
Russell Santos

@RussellSantos 객체는 특정 시점에서 가비지 수집이 필요하다고 가정합니다. 지적 해 주셔서 감사합니다. 내 예제에서는 destroy()객체를 제거해야 할 때마다 사용 코드로 호출 해야하는 메소드를 추가했습니다 .
NanoWizard

4

예, 완전히 할 수 있고 매우 쉽습니다. 생성자에서 프로토 타입 객체 그래프를 반환하여 개인 변수와 함수를 노출하면됩니다. 이것은 새로운 것이 아니지만 그것의 우아함을 이해하기 위해 약간의 js foo를 사용하십시오. 이 방법은 전역 범위 또는 취약점 맵을 사용하지 않습니다. 언어에 반영된 형태입니다. 이것을 활용하는 방법에 따라; 호출 스택을 방해하는 예외를 강제로 수행하거나 예외를로 저장할 수 있습니다 undefined. 이것은 아래에서 시작되며 이러한 기능에 대한 자세한 내용은 여기를 참조하십시오.

class Clazz {
  constructor() {
    var _level = 1

    function _private(x) {
      return _level * x;
    }
    return {
      level: _level,
      public: this.private,
      public2: function(x) {
        return _private(x);
      },
      public3: function(x) {
        return _private(x) * this.public(x);
      },
    };
  }

  private(x) {
    return x * x;
  }
}

var clazz = new Clazz();

console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //1
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error


3
class Something {
  constructor(){
    var _property = "test";
    Object.defineProperty(this, "property", {
        get: function(){ return _property}
    });
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"

2
코드 전용 답변을 피하는 것이 가장 좋습니다. 코드가 OP의 질문에 어떻게 대답하는지 설명 할 수 있다면 더 좋을 것입니다
Stewart_R

이것은 실제로 전용 변수보다 읽기 전용 변수를 만드는 방법입니다. 개인 변수는 외부에서 액세스 할 수 없어야합니다. console.log(instance.property)"테스트"를 돌려주지 말고 던지거나 정의하지 말아야합니다.
oooyaya

3

마지막 두 게시와 비슷한 다른 방법

class Example {
  constructor(foo) {

    // privates
    const self = this;
    this.foo = foo;

    // public interface
    return self.public;
  }

  public = {
    // empty data
    nodata: { data: [] },
    // noop
    noop: () => {},
  }

  // everything else private
  bar = 10
}

const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined

2

대부분의 답변은 불가능하다고 말하거나 폴리 필이 필요할 수있는 ES6 기능인 WeakMap 또는 Symbol을 사용해야합니다. 그러나 다른 방법이 있습니다! 이것을 확인하십시오 :

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

이 메서드 접근 자 패턴이라고 합니다. 근본적인 아이디어는 우리가 가지고있다 폐쇄 하는 폐쇄 내부를, 그리고 우리는 만들 개인 개체를 당신이있는 경우에만 액세스 할 수 (생성자) 키를 .

관심이 있으시면 내 기사 에서 이에 대해 더 많이 읽을 수 있습니다 . 이 방법을 사용하면 클로저 외부에서 액세스 할 수없는 객체 별 속성을 만들 수 있습니다. 따라서 생성자 또는 프로토 타입에서 사용할 수 있지만 다른 곳에서는 사용할 수 없습니다. 나는이 방법이 어디서나 사용되는 것을 보지 못했지만 실제로 강력하다고 생각합니다.


문제는 ES6 수업에서 이것을 달성하는 방법에 관한 것이 었습니다.
Michael Franzl

ES6 클래스에서 똑같은 방법을 사용할 수 있습니다. ES6 수업은 주로 내 예에서 제시 한 것처럼 함수 위에 설탕입니다. 원래 포스터가 트랜스 파일러를 사용하고있을 가능성이 있으며,이 경우 WeakMaps 또는 Symbols에는 여전히 폴리 필이 필요합니다. 내 대답은 관계없이 유효합니다.
guitarino

2

프라이빗 및 퍼블릭 인터페이스를 갖춘 깨끗하고 간단한 '클래스'솔루션과 컴포지션 지원에 대해서는 이 답변 을 참조하십시오.


2

매우 간단한 해결책을 찾았습니다 Object.freeze(). 물론 문제는 나중에 객체에 아무것도 추가 할 수 없다는 것입니다.

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode

이것은 또한 다음과 같은 setter 메소드를 비활성화합니다.setName(name) { this.name = name; }
ngakak

2

이 패턴을 사용하며 항상 나를 위해 일했습니다.

class Test {
    constructor(data) {
        class Public {
            constructor(prv) {

                // public function (must be in constructor on order to access "prv" variable)
                connectToDb(ip) {
                    prv._db(ip, prv._err);
                } 
            }

            // public function w/o access to "prv" variable
            log() {
                console.log("I'm logging");
            }
        }

        // private variables
        this._data = data;
        this._err = function(ip) {
            console.log("could not connect to "+ip);
        }
    }

    // private function
    _db(ip, err) {
        if(!!ip) {
		    console.log("connected to "+ip+", sending data '"+this.data+"'");
			return true;
		}
        else err(ip);
    }
}



var test = new Test(10),
		ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined


2

사실 그것은 이다 가능.
1. 먼저 클래스를 만들고 생성자에서 호출 된 _public함수를 반환합니다 .
2. 호출 된 _public함수에서 this참조 (모든 개인 메소드 및 소품에 대한 액세스 권한을 얻기 위해) 및 모든 인수 (에서 constructor 전달됨 new Names())를 전달하십시오.
3. _public함수 범위에는 (_this에 Names대한 액세스 권한이 있는 클래스 도 있습니다. this) 개인 Names클래스의 참조

class Names {
  constructor() {
    this.privateProperty = 'John';
    return _public(this, arguments);
  }
  privateMethod() { }
}

const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind

function _public(_this, _arguments) {
  class Names {
    constructor() {
      this.publicProperty = 'Jasmine';
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

    somePublicMethod() {
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

  }
  return new Names(..._arguments);
}

2

당신은 이것을 시도 할 수 있습니다 https://www.npmjs.com/package/private-members

이 패키지는 멤버를 인스턴스별로 저장합니다.

const pvt = require('private-members');
const _ = pvt();

let Exemplo = (function () {    
    function Exemplo() {
        _(this).msg = "Minha Mensagem";
    }

    _().mensagem = function() {
        return _(this).msg;
    }

    Exemplo.prototype.showMsg = function () {
        let msg = _(this).mensagem();
        console.log(msg);
    };

    return Exemplo;
})();

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