programing

JavaScript ES6 클래스의 개인 속성

newsource 2022. 9. 18. 12:49

JavaScript ES6 클래스의 개인 속성

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

여기 예가 있어요. ' 금지'를 수 요?instance.property

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

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

업데이트: 다른 사용자의 답변을 참조하십시오. 이것은 구식입니다.

간단히 말하면, ES6 클래스가 있는 개인 자산은 기본적으로 지원되지 않습니다.

그러나 새 속성을 개체에 연결하지 않고 클래스 생성자 내에 유지하고 getters와 setters를 사용하여 숨겨진 속성에 도달함으로써 이러한 동작을 흉내낼 수 있습니다.getters와 setters는 클래스의 새로운 인스턴스마다 재정의됩니다.

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

프라이빗 클래스 기능은 스테이지3 제안서에 있습니다.대부분의 기능은 모든 주요 브라우저에서 지원됩니다.

class Something {
  #property;

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

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

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

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> test
console.log(instance.#property); //=> Syntax error

이름 에 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 네.#컨스트럭터뿐만 아니라 클래스 정의에 포함시킵니다.

드디어 ES2022에 실제 사유지가 추가되었습니다.2022-03-22 현재 모든 주요 브라우저에서 최소 6개월 이상 개인 속성(필드 및 메서드)이 지원되고 있으나 사용자의 10~20%는 이전 브라우저 [Can I Use]에 남아 있습니다.

예:

class Person {
  #age

  constructor(name) {
    this.name = name; // this is public
    this.#age = 20; // this is 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 name but not age

다음은 ES2022 이전 환경에서 다양한 트레이드오프와 함께 자산을 비공개로 유지하는 방법입니다.

범위 변수

여기서 접근법은 비공개인 생성자 함수의 범위를 사용하여 개인 데이터를 저장하는 것입니다.메서드가 이 개인 데이터에 액세스할 수 있도록 하려면 생성자 내부에서도 이러한 데이터를 생성해야 합니다. 즉, 모든 인스턴스에서 해당 데이터를 다시 생성해야 합니다.이는 퍼포먼스와 메모리의 불이익이지만 허용될 수 있습니다.통상적인 방법으로 개인 데이터에 액세스 할 필요가 없는 방법에 대해서는 패널티를 회피할 수 있습니다.

예:

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

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

  anotherMethod() {
    // here we can access name but not age
  }
}

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

// here we can access name but not age

스코프 취약 맵

WeakMap을 사용하면 위의 접근 방식을 더욱 복잡하게 만드는 대신 성능을 향상시킬 수 있습니다.WeakMaps는 해당 WeakMap을 통해서만 액세스할 수 있는 방식으로 데이터를 개체(여기서는 클래스 인스턴스)와 연결합니다. WeakMap을후이하여 "WeakMap"과 .this모든 인스턴스가 하나의 WeakMap을 공유할 수 있기 때문에 단순히 자신의 WeakMap에 액세스하기 위해 메서드를 재작성할 필요가 없기 때문에 범위 변수 방식보다 빠릅니다.

예:

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

  return 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}`);
    }
  };
})();

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

// here we can access name but not age

하여 여러 의 WeakMap을 합니다.의 WeakMap을 하여 WeakMap과 같이 사용할 .또한 여러 개의 WeakMap을 사용하여 다음과 같이 사용할 수도 있습니다.privateAge.set(this, 20) 작은 으로 privateProps.set(this, 'age', 0).

침해될 .WeakMap 글로벌에 깨질 수 .JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript, JavaScript.

(이은 (이렇게 하다)로 할 MapWeakMap더 좋은 이유는Map매우 주의하지 않으면 메모리 누수가 발생합니다.))), )))))))))))))))))))))))

하프 앤서: 스코프 기호

기호는 문자열 대신 속성 이름으로 사용할 수 있는 원시 값의 유형입니다.기호를 개인 를 개인 기호로 할 수 .this[mySymbol].

될 수 .Object.getOwnPropertySymbols

예:

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

  return 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]}`);
    }
  }
})();

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

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

non음음 note note note note note note note note note note note note note note note note note note note note note note를 사용하여 속성을 열거할 수 만드는 것에 주의해 .Object.defineProperty에 포함되는 것을 막지는 않습니다.Object.getOwnPropertySymbols.

반답:밑줄

기존 관습은 언더스코어 접두사가 있는 공용 속성만 사용하는 것입니다.이것은 그것을 비공개로 유지하지는 않지만, 독자들에게 그들이 그것을 비공개로 다루어야 한다는 것을 잘 전달하고, 이것은 종종 그 일을 해낸다.그 대신에, 다른 회피책보다 읽기 쉽고, 타이핑하기 쉽고, 고속의 어프로치를 얻을 수 있습니다.

예:

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.

요약

  • ES2022: 훌륭하지만 모든 방문객이 아직 지원하지는 않음
  • 범위 변수: 프라이빗, 슬로우, 어색함
  • Scope Weak Maps: 해킹하기 쉽고 어색함
  • 범위부호: 열거할 수 있고 해킹할 수 있으며 다소 어색함
  • 밑줄: 사생활에 대한 요청일 뿐 다른 단점은 없습니다.

업데이트: 더 나은 구문을 가진 제안서가 발송 중입니다.기부를 환영합니다.


네, 오브젝트에는 범위 지정 액세스가 있습니다.ES6에서는 를 도입하고 있습니다.

기호는 고유합니다.반사(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

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

  • 모듈을 사용합니다. 한 입니다.export키워드를 지정합니다.
  • 모듈 내에서는 함수 클로저를 사용합니다.http://www.kirupa.com/html5/closures_in_javascript.htm

(기호를 사용하여 프라이버시를 보장할 수 있다는 제안은 ES6 사양의 이전 버전에서는 사실이었지만 https://stackoverflow.com/a/22280202/1282216 및 https://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}`)
      }
    }
  }
}

누구에게 묻느냐에 따라 다릅니다:-)

.private속성 수식자는 현재 초안으로 작성된 것으로 보이는 Maximally minimum 클래스 제안에 포함되어 있습니다.

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

ES6 모듈(처음 @d13에서 제안)을 사용하는 것이 좋습니다.개인 소유물을 완벽하게 모방하지는 않지만, 최소한 개인 소유물이 교실 밖으로 유출되지는 않을 것이라는 확신을 가질 수 있습니다.다음은 예를 제시하겠습니다.

something.something.module

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이 코멘트에서 설명한 바와 같이 이러한 개인 속성은 정적이기 때문에 범위가 글로벌합니다.싱글톤으로 작업할 때는 잘 작동하지만 Transient 객체에 주의해야 합니다.위의 예를 확장합니다.

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

- 캡슐화된 속성을 만들있지만 적어도 ES6에서는 액세스 수식자(public|private)를 사용하지 않습니다.

ES6를 사용한 간단한 예를 다음에 나타냅니다.

1 클래스 워드를 사용하여 클래스를 만듭니다.

2 컨스트럭터 내부에서는 let OR consted reserved words ->를 사용하여 블록 범위 변수를 선언합니다.블록 범위이기 때문에 외부에서 접근할 수 없습니다(캡슐화).

명령을 사용하여합니다.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

@d13과 @johnny-oshika 및 @DanyalAytekin의 코멘트를 완성합니다.

@ 일반 하고 @johnny-oshika를 사용할 수 것 ..bind에 "a"를 더한 것_privates오브젝트를 큐레이션된 파라미터로 지정합니다.

something.something.module

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.discloss.main.discloss.

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 표시

생각할 수 있는 단점은 다음과 같습니다.

  • 직관성이 낮다
  • 클래스 구문과 오래된 학교 패턴의 혼합 사용(개체 바인딩, 모듈/함수 범위 변수)
  • 하드 바인딩 - 공개 메서드를 재바인딩할 수 없습니다(소프트 바인딩을 사용하면 이를 개선할 수 있습니다(https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#softening-binding))).

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

'프라이빗'에 대한 다른 접근법

현재 ES6에서는 프라이빗 가시성을 이용할 수 없다는 사실에 대항하는 대신, IDE가 JSDoc(Webstorm 등)를 지원한다면 문제없이 사용할 수 있는 보다 실용적인 접근방식을 채택하기로 결정했습니다.태그를 사용하는 것이 목적입니다.개발에 관한 한 IDE는 클래스 외부에서 개인 멤버에 액세스할 수 없도록 합니다.이 기능은 나에게 매우 효과적이며 내부 메서드를 숨기는 데 매우 유용하기 때문에 자동 완성 기능을 통해 클래스가 실제로 무엇을 보여주는지 알 수 있습니다.다음은 예를 제시하겠습니다.

공개 내용만 자동으로 표시

오, 정말 많은 이국적인 해결책들이군요!저는 평소에 사생활에 관심이 없기 때문에 여기 나와 있는 처럼 '의사 사생활'을 사용하고 있습니다.다만, 이것에 특별한 요건이 있는 경우는, 다음과 같은 것을 사용합니다.

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

Weak Map(위크맵)

  • IE11에서 지원(심볼은 지원되지 않음)
  • 는 hard-private(하드-프라이빗)에이 됩니다).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에서 지원되지 않지만 예제에서는 더 깨끗해 보입니다.

이 게시물은 '수업용 개인 데이터'의 베스트 프랙티스를 찾다가 우연히 알게 되었습니다.일부 패턴은 성능 문제가 있다고 언급되었습니다.

저는 온라인 책 "Explying ES6"의 4가지 주요 패턴을 바탕으로 몇 가지 jsperf 테스트를 준비했습니다.

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

테스트는 다음 URL에서 확인할 수 있습니다.

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

Chrome 63.0.3239/Mac OS X 10.11.6에서 최고의 성능 패턴은 "컨스트럭터 환경을 통한 개인 데이터"와 "이름 지정 규칙을 통한 개인 데이터"였습니다.저는 WeakMap은 Safari가 잘 작동했지만 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();

개인적으로 바인드 연산자의 제안을 좋아합니다. :: @d13의 @d13을 합니다.exportprivate에 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

댓글 달아주시면 감사하겠습니다.

언어가 명시적으로 사적인 변수를 지원할 때까지 대부분의 경우 벤자민의 답변이 최선이라고 생각합니다.

단, 어떤 이유로 접근을 금지할 필요가 있는 경우 구성 시 속성 식별자로 사용할 수 있는 고유한 구성 불가, 열거 불가, 쓰기 불가 속성을 각 오브젝트에 부가하는 방법을 검토했습니다(예: 고유).Symbol'다니다'와 독특한 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"

를 사용하는 것에 비해 이 접근방식의 잠재적인 장점은 퍼포먼스가 우려되는 경우 액세스 시간이 단축된다는 것입니다.

나는 건설업자 내부의 폐쇄를 이용하여 '양쪽 세계'를 모두 얻을 수 있다고 믿는다.두 가지 종류가 있습니다.

모든 데이터 구성원은 비공개입니다.

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

실제로 기호와 프록시를 사용할 수 있습니다.의 기호를 에 의해 Reflect 하나는 , 「 」는 「 」의 2」입니다.「Reflect.ownKeys(」) 오브젝트.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))그래서 이 물체들을 위한 함정이 필요한 겁니다

타이프 스크립트도 할 수 없어요.문서 내용:

구성원이 개인으로 표시된 경우 해당 구성원이 포함된 클래스 외부에서 액세스할 수 없습니다.예를 들어 다음과 같습니다.

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

그래서 그들의 "프라이빗" 키워드는 효과가 없다.

파티에 너무 늦게 오는데 검색에서 작전 질문을 쳤기 때문에...예, 클래스 선언을 마감으로 래핑하여 개인 자산을 가질 수 있습니다.

코드펜에는 어떻게 개인 메서드를 가지고 있는지에 대한 예가 있습니다.아래 토막에서 Subscribable 클래스에는 두 가지 '개인' 함수가 있습니다.process ★★★★★★★★★★★★★★★★★」processCallbacks모든 속성은 이러한 방법으로 추가할 수 있으며 폐쇄를 통해 비공개로 유지됩니다.IMO Privacy는 문제가 잘 구분되어 있고 Javascript가 깔끔하게 닫혔을 때 구문을 추가함으로써 비대해질 필요가 없다면 매우 드문 요구입니다.

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

저는 이 접근방식이 관심사를 잘 구분하고 사안을 철저히 비밀에 부치기 때문에 좋아합니다.유일한 단점은 사적인 콘텐츠에서 'self'(또는 이와 유사한 것)를 'this'(이것)로 지칭할 필요가 있다는 것입니다.

아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 맞다.이 작업은 생성자에서 프로토타입 개체 그래프를 반환하여 개인 변수와 함수를 노출함으로써 수행됩니다.이것은 새로운 것이 아니지만, 그것의 우아함을 이해하기 위해 약간의 jsfoo를 가져보세요.이 방법에서는 글로벌스코프 또는 weakmap을 사용하지 않습니다.그것은 언어에 내장된 성찰의 한 형태이다.이것을, 콜하거나, 를 「」로서 할 수 .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

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"

지난 두 건의 게시물과 비슷한 또 다른 방법

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

해결책을 요, 하세요.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

이 코드는 프라이빗과 퍼블릭, 스태틱과 비 스태틱, 인스턴스와 클래스 수준, 변수, 메서드 및 속성을 나타냅니다.

https://codesandbox.io/s/class-demo-837bj

class Animal {
    static count = 0 // class static public
    static #ClassPriVar = 3 // class static private

    constructor(kind) {
        this.kind = kind // instance public property
        Animal.count++
        let InstancePriVar = 'InstancePriVar: ' + kind // instance private constructor-var
        log(InstancePriVar)
        Animal.#ClassPriVar += 3
        this.adhoc = 'adhoc' // instance public property w/out constructor- parameter
    }

    #PawCount = 4 // instance private var

    set Paws(newPawCount) {
        // instance public prop
        this.#PawCount = newPawCount
    }

    get Paws() {
        // instance public prop
        return this.#PawCount
    }

    get GetPriVar() {
        // instance public prop
        return Animal.#ClassPriVar
    }

    static get GetPriVarStat() {
        // class public prop
        return Animal.#ClassPriVar
    }

    PrintKind() {
        // instance public method
        log('kind: ' + this.kind)
    }

    ReturnKind() {
        // instance public function
        return this.kind
    }

    /* May be unsupported

    get #PrivMeth(){  // instance private prop
        return Animal.#ClassPriVar + ' Private Method'
    }

    static get #PrivMeth(){  // class private prop
        return Animal.#ClassPriVar + ' Private Method'
    }
    */
}

function log(str) {
    console.log(str)
}

// TESTING

log(Animal.count) // static, avail w/out instance
log(Animal.GetPriVarStat) // static, avail w/out instance

let A = new Animal('Cat')
log(Animal.count + ': ' + A.kind)
log(A.GetPriVar)
A.PrintKind()
A.Paws = 6
log('Paws: ' + A.Paws)
log('ReturnKind: ' + A.ReturnKind())
log(A.adhoc)

let B = new Animal('Dog')
log(Animal.count + ': ' + B.kind)
log(B.GetPriVar)
log(A.GetPriVar) // returns same as B.GetPriVar. Acts like a class-level property, but called like an instance-level property. It's cuz non-stat fx requires instance.

log('class: ' + Animal.GetPriVarStat)

// undefined
log('instance: ' + B.GetPriVarStat) // static class fx
log(Animal.GetPriVar) // non-stat instance fx
log(A.InstancePriVar) // private
log(Animal.InstancePriVar) // private instance var
log('PawCount: ' + A.PawCount) // private. Use getter
/* log('PawCount: ' + A.#PawCount) // private. Use getter
log('PawCount: ' + Animal.#PawCount) // Instance and private. Use getter */

이전 답변을 읽고 이 예제가 위의 해결책을 요약할 수 있다고 생각했습니다.

const friend = Symbol('friend');

const ClassName = ((hidden, hiddenShared = 0) => {

    class ClassName {
        constructor(hiddenPropertyValue, prop){
            this[hidden] = hiddenPropertyValue * ++hiddenShared;
            this.prop = prop
        }

        get hidden(){
            console.log('getting hidden');
            return this[hidden];
        }

        set [friend](v){
            console.log('setting hiddenShared');
            hiddenShared = v;
        }

        get counter(){
            console.log('getting hiddenShared');
            return hiddenShared;
        }

        get privileged(){
            console.log('calling privileged method');
            return privileged.bind(this);
        }
    }

    function privileged(value){
        return this[hidden] + value;
    }

    return ClassName;
})(Symbol('hidden'), 0);

const OtherClass = (() => class OtherClass extends ClassName {
    constructor(v){
        super(v, 100);
        this[friend] = this.counter - 1;
    }
})();

갱신하다

이제 진정한 개인 속성 및 메서드를 만들 수 있습니다(적어도 현재로서는 크롬 기반 브라우저).

구문이 꽤 깔끔하네요

class MyClass {
    #privateProperty = 1
    #privateMethod() { return 2 }
    static #privateStatic = 3
    static #privateStaticMethod(){return 4}
    static get #privateStaticGetter(){return 5}

    // also using is quite straightforward
    method(){
        return (
            this.#privateMethod() +
            this.#privateProperty +
            MyClass.#privateStatic +
            MyClass.#privateStaticMethod() +
            MyClass.#privateStaticGetter
        )
    }
}

new MyClass().method()
// returns 15

할 때는, 「스태틱 레퍼런스」를 사용하지 않는 해 주세요.this.constructor.#private왜냐하면 그 하위 분류에 제동이 걸리기 때문이다.의 정적 개인 의 메서드 를.MyClass.#private.

대부분의 답변은 불가능하다고 답하거나 폴리필이 필요할 수 있는 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

이것을 메서드액세서 패턴이라고 부릅니다기본적으로는 폐쇄 내부에 키가 있으며, 키가 있는 경우에만 액세스할 수 있는 개인 개체를 생성자에 만듭니다.

관심이 있으시다면 제 기사에서 더 읽어보실 수 있습니다.이 방법을 사용하면 폐쇄 외부에서 액세스할 수 없는 객체별 특성을 작성할 수 있습니다.따라서 생성자 또는 프로토타입에서는 사용할 수 있지만 다른 곳에서는 사용할 수 없습니다.이 방법은 어디서도 본 적이 없지만, 매우 강력한 것 같습니다.

프라이빗 및 퍼블릭 인터페이스와 구성을 지원하는 깨끗하고 심플한 '클래스' 솔루션에 대해서는 이 답변을 참조하십시오.

나는 이 패턴을 사용했고 항상 나에게 효과가 있었다.

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

언급URL : https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes