ES6 클래스에서 개인 속성을 만들 수 있습니까?
다음은 예입니다. 에 대한 액세스를 방지하려면 어떻게해야 instance.property
합니까?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
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
this
전화하기 전에 생성자에서 사용할 수 없습니다 super()
. 그러나 babel은 그것들을 슈퍼보다 먼저 넣습니다.
#privateCrap
구문 을 허용하도록 ESLint를 구성하는 방법은 무엇입니까?
#beep() {}
; 그리고 이것 : async #bzzzt() {}
?
짧은 대답은 아닙니다. 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; }
}
class
구문이 처음부터 장점을 크게 손상 시킨다는 것입니다 .
getName
및 setName
속성을 어떻게 비공개로 설정합니까?
@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은 범위가 지정된 변수보다 매우 개인적이며 더 실용적입니다. 범위가 지정된 기호는 합리적으로 사적이며 합리적입니다. 밑줄은 종종 충분히 사적이며 매우 실용적입니다.
instanceof
. 나는 그 접근법이 완전성을 위해서만 포함 된 것으로 생각하고 있으며 그것이 실제로 얼마나 능력이 있는지에 대해 더 많은 생각을했을 것입니다.
업데이트 : 더 좋은 구문 의 제안 이 진행 중입니다. 기부를 환영합니다.
예, 객체에 범위가 지정된 액세스가 있습니다 . ES6에 Symbol
s가 도입되었습니다 .
심볼은 고유하며 리플렉션 (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
Object.getOwnPropertySymbols
? ;)
const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();
것은. 변수를 캡슐화하기 위해 클로저를 사용하는 것을 의미하는 "비공개"JavaScript를 고려할 것입니다. 따라서 이러한 변수는 리플렉션을 통해 액세스 할 수 없습니다.
private
and protected
키워드 를 사용하는 것이 Symbol
or 보다 훨씬 더 깨끗 하다고 생각합니다 Name
. 대괄호 표기법보다는 점 표기법을 선호합니다. 나는 사적인 것들에 점을 계속 사용하고 싶습니다. this.privateVar
대답은 '아니오". 그러나 다음과 같은 속성에 대한 개인 액세스 권한을 만들 수 있습니다.
export
키워드 를 사용하여 공개하지 않는 한 모듈의 모든 내용은 비공개 입니다.ES6 사양의 이전 버전에서는 프라이버시를 보장하기 위해 Symbols를 사용할 수 있다는 제안은 더 이상 사실이 아닙니다 : https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604 HTML 및 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));
}
}
get
수를 방법 당 하나로 줄일 수도 있습니다 (예 :) const _ = privates.get(this); console.log(_.privateProp1);
.
const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
는 귀하의 재산이 비공개인지 아닌지 를 의미합니까?
나중에 다른 조회자를 참조 하기 위해 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
속성 수정은 포함되지 않습니다 극대 최소한의 클래스 제안 에 그것을 만든 것 같습니다 현재 초안 .
그러나 private 속성을 허용하는 private 이름 이 지원 될 수 있으며 클래스 정의에도 사용될 수 있습니다.
ES6 모듈 (@ d13에서 처음 제안)을 사용하면 나에게 잘 작동합니다. 개인 속성을 완벽하게 모방하지는 않지만 최소한 개인 속성이 클래스 외부로 누출되지 않을 것이라고 확신 할 수 있습니다. 예를 들면 다음과 같습니다.
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
private static
.
a.say(); // a
는 다음과 같아야합니다.b.say(); // b
let _message = null
호출 생성자를 여러 번 할 때 너무 시원하지 않은 방법으로 시도 하면 엉망이됩니다.
@ 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
예-캡슐화 된 속성을 만들 수 있습니다 있지만 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
new Something();
당신의 메소드가 생성자에서 선언되어 있기 때문에 호출 할 때마다 각 인스턴스 메소드의 사본이 생성된다는 사실에도 불구하고 귀하의 솔루션은 개인 변수를 달성하는 가장 좋은 타협이라고 말했습니다. 개인 변수. 클래스의 인스턴스를 많이 만들면 많은 메모리 소비가 발생할 수 있으므로 성능 문제가 발생합니다. 메소드는 생성자 범위 밖에서 선언되어야합니다. 내 의견은 비판보다 솔루션 단점에 대한 설명이었습니다.
ES6에서는 개인 가시성을 현재 사용할 수 없다는 사실과 싸우는 대신 IDE가 JSDoc (예 : Webstorm)을 지원하는 경우에는 더 실용적인 접근 방식을 사용하기로 결정했습니다. 아이디어는 @private
태그 를 사용하는 것 입니다. 개발이 진행되는 한 IDE는 클래스 외부에서 개인 멤버에 액세스하지 못하게합니다. 나를 위해 잘 작동하고 내부 메서드를 숨기는 데 실제로 유용하므로 자동 완성 기능은 클래스가 실제로 노출하려고하는 것을 보여줍니다. 예를 들면 다음과 같습니다.
@private
의견은 이러한 것들을 막을 수 없으며 문서 생성을위한 기능 일 뿐이며 IDE입니다.
약한지도
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에서 지원되지 않지만 예제에서는 더 깔끔해 보입니다.
아, 너무 많은 이국적인 솔루션! 나는 보통 프라이버시를 신경 쓰지 않기 때문에 여기에 언급 된 것처럼 "의사 프라이버시" 를 사용합니다 . 그러나주의를 기울이면 (특별한 요구 사항이있는 경우)이 예제와 같은 것을 사용합니다.
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)
}
개인적으로 나는 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
그것에 대한 의견을 부탁드립니다.
"클래스 개인 데이터"에 대한 모범 사례를 찾을 때이 게시물을 보았습니다. 일부 패턴에는 성능 문제가있을 수 있다고 언급했습니다.
온라인 책 "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();
나는 생성자 내부의 클로저를 사용하여 '두 세계의 최고'를 얻는 것이 가능하다고 생각합니다. 두 가지 변형이 있습니다.
모든 데이터 멤버는 비공개입니다
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.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))
합니다. 그래서 우리는 이러한 객체에 대한 트랩이 필요합니다.
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);
따라서 "비공개"키워드는 효과가 없습니다.
이 파티에 매우 늦었지만 검색에서 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'(또는 이와 유사한 것)를 사용해야한다는 것입니다.
벤자민의 대답 은 아마도 언어가 명시 적으로 개인 변수를 지원할 때까지 대부분의 경우에 가장 적합 하다고 생각 합니다.
그러나 어떤 이유로으로 액세스를 방지 해야하는 경우 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
것 보다 잠재적 인 이점은 액세스 시간 이 더 빠릅니다 .
destroy()
객체를 제거해야 할 때마다 사용 코드로 호출 해야하는 메소드를 추가했습니다 .
예, 완전히 할 수 있고 매우 쉽습니다. 생성자에서 프로토 타입 객체 그래프를 반환하여 개인 변수와 함수를 노출하면됩니다. 이것은 새로운 것이 아니지만 그것의 우아함을 이해하기 위해 약간의 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
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"
console.log(instance.property)
"테스트"를 돌려주지 말고 던지거나 정의하지 말아야합니다.
마지막 두 게시와 비슷한 다른 방법
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
대부분의 답변은 불가능하다고 말하거나 폴리 필이 필요할 수있는 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
이 메서드 접근 자 패턴이라고 합니다. 근본적인 아이디어는 우리가 가지고있다 폐쇄 하는 키 폐쇄 내부를, 그리고 우리는 만들 개인 개체를 당신이있는 경우에만 액세스 할 수 (생성자) 키를 .
관심이 있으시면 내 기사 에서 이에 대해 더 많이 읽을 수 있습니다 . 이 방법을 사용하면 클로저 외부에서 액세스 할 수없는 객체 별 속성을 만들 수 있습니다. 따라서 생성자 또는 프로토 타입에서 사용할 수 있지만 다른 곳에서는 사용할 수 없습니다. 나는이 방법이 어디서나 사용되는 것을 보지 못했지만 실제로 강력하다고 생각합니다.
매우 간단한 해결책을 찾았습니다 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
setName(name) { this.name = name; }
이 패턴을 사용하며 항상 나를 위해 일했습니다.
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
사실 그것은 이다 가능.
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);
}
당신은 이것을 시도 할 수 있습니다 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;