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標準で実装されています。バベル7とステージ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()
。しかし、バベルはスーパーの前にそれらを置きます。
#privateCrap
構文を許可するようにESLintを構成する方法
#beep() {}
; そしてこれ:async #bzzzt() {}
?
短い回答ですが、ES6クラスのプライベートプロパティはネイティブでサポートされていません。
ただし、オブジェクトに新しいプロパティをアタッチせずに、クラスコンストラクター内に保持することでその動作を模倣し、ゲッターとセッターを使用して非表示のプロパティにアクセスできます。ゲッターとセッターは、クラスの新しいインスタンスごとに再定義されることに注意してください。
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を使用して、以前のアプローチのパフォーマンスとメモリのペナルティを回避できます。WeakMapは、そのWeakMapを使用しないとアクセスできない方法で、データをオブジェクト(ここではインスタンス)に関連付けます。したがって、スコープ変数メソッドを使用してプライベートWeakMapを作成し、そのWeakMapを使用してに関連付けられたプライベートデータを取得しますthis
。すべてのインスタンスが1つの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
この例では、オブジェクトを使用して、複数のプライベートプロパティに1つのWeakMapを使用しています。また、複数のWeakMapを使用してそれらをのようage.set(this, 20)
に使用したり、小さなラッパーを作成して別の方法で使用したりすることもできますprivateProps.set(this, 'age', 0)
。
このアプローチのプライバシーは、理論的にはグローバルWeakMap
オブジェクトを改ざんすることによって侵害される可能性があります。そうは言っても、すべてのJavaScriptはマングルされたグローバルによって破壊される可能性があります。私たちのコードは、これが起こらないことを前提にすでに構築されています。
(この方法は、でも実行できますが、非常に注意しない限り、メモリリークが発生するためMap
、この方法のWeakMap
方が優れてMap
います。この目的のために、この2つに違いはありません。)
シンボルは、プロパティ名として使用できるプリミティブ値の一種です。スコープ変数メソッドを使用してプライベートシンボルを作成し、プライベートデータをに格納できます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
秒。
シンボルは一意であり、リフレクション(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の意味でのあいまいさです。「プライベート」JavaScriptは、クロージャーを使用して変数をカプセル化することを意味すると考えます。したがって、これらの変数はリフレクションを通じてアクセスできません。
private
とprotected
、Symbol
またはよりもはるかにクリーンになると思いName
ます。ブラケット表記よりもドット表記の方が好きです。プライベートにはドットを使い続けたいです。this.privateVar
答えはノーだ"。ただし、次のようなプロパティへのプライベートアクセスを作成できます。
export
キーワードを使用して公開しない限り、モジュール内のすべてがプライベートです。(プライバシーを確保するためにシンボルを使用できるという提案は、ES6仕様の以前のバージョンでは真実でしたが、現在は当てはまりません: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
メソッドごとに1つに減らすこともできます(例:)const _ = privates.get(this); console.log(_.privateProp1);
。
const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
それはあなたの財産が私的であるかどうかを意味しますか?
他のLookerを将来参照するために、WeakMapを使用してプライベートデータを保持することをお勧めしていると聞いています。
以下は、より明確で実用的な例です。
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
プロパティの修飾子は、現在のドラフトに採用されたように見える、最小限のクラスの提案には含まれていません。
ただし、プライベートプロパティを許可するプライベート名のサポート が存在する可能性があり、おそらくクラス定義でも使用できます。
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でこれを行う方法の簡単な例を次に示します。
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です。
WeakMap
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)
}
個人的にはバインド演算子の 提案が好きで、::
それを前述のソリューション@ d13と組み合わせますが、今のところexport
、クラスのキーワードを使用してモジュールにプライベート関数を配置する@ d13の回答に固執します。
ここでは触れられていない、より機能的なアプローチであり、クラス内にすべてのプライベートプロップ/メソッドを持つことができる、もう1つの難しいソリューションがあります。
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();
私は、コンストラクター内のクロージャーを使用して「両方の長所」を得ることが可能であると信じています。2つのバリエーションがあります。
すべてのデータメンバーはプライベートです
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);
}
};
実際、シンボルとプロキシを使用することは可能です。クラススコープでシンボルを使用し、プロキシに2つのトラップを設定します。1つはクラスプロトタイプ用で、Reflect.ownKeys(instance)またはObject.getOwnPropertySymbolsがシンボルを提供しないようにします。もう1つはコンストラクタ自体用です。したがって、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の質問にヒットしました... はい、クラス宣言をクロージャーでラップすることにより、プライベートプロパティを持つことができます
このcodepenにプライベートメソッドがある方法の例があります。以下のスニペットでは、Subscribableクラスに2つの「プライベート」関数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)}}}})();
私はこのアプローチが好きです。懸念をうまく分離し、物事を本当にプライベートに保つからです。唯一の欠点は、プライベートコンテンツで「これ」を参照するために「自己」(または類似の何か)を使用する必要があることです。
ベンジャミンの答えは、言語が明示的にプライベート変数をネイティブでサポートするまで、ほとんどの場合におそらく最良だと思います。
ただし、何らかの理由ででのアクセスを防止する必要がある場合、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"
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)
「テスト」を返さないでください。
投稿された最後の2つに似た別の方法
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
ほとんどの回答は不可能であるか、WeakMapまたはSymbolを使用する必要があります。これらは、おそらくポリフィルを必要とするES6の機能です。ただし、別の方法があります。これをチェックしてください:
// 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;