web-dev-qa-db-ja.com

JavaScript ES6クラスのプライベートプロパティ

ES6クラスでプライベートプロパティを作成することは可能ですか?

これが一例です。どうやってinstance.propertyへのアクセスを防ぐことができますか?

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

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

プライベートフィールドは ECMA標準 に実装されています。今日は babel 7 とステージ3のプリセットを使って使い始めることができます。 babel REPL example を参照してください。

class Something {
  #property;

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

const instance = new Something();
console.log(instance.property); //=> undefined
89
Alister

簡単な答え、いいえ、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; }
}
237
MetalGodwin

@ 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を使用した場合にのみアクセスできるように、データをオブジェクト(ここではインスタンス)と関連付けます。そのため、スコープ付き変数メソッドを使用してプライベート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を使用して、複数のプライベートプロパティに対して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)`.

半回答:アンダースコア

古いデフォルトでは、アンダースコアの接頭辞を付けたpublicプロパティを使うだけです。いかなる意味においても私有財産ではないが、この慣習は読者がその財産を私有財産として扱うべきであることを伝える良い仕事をするほど十分に普及している。この経過と引き換えに、読みやすく、入力しやすく、そしてより速いアプローチが得られます。

例:

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はスコープ付き変数よりも非常に非公開で実用的です。スコープ付きシンボルは適度に非公開で適度に実用的です。アンダースコアは、十分に非公開で非常に実用的です。

168
twhb

更新: より良い構文の提案 が登場しました。貢献は大歓迎です。


はい、あります - オブジェクト内のスコープアクセスのために - { ES6ではSymbols があります。

シンボルはユニークで、リフレクションを除いて外側からシンボルにアクセスすることはできません(Java/C#のprivatesのように)が、内側のシンボルにアクセスできる人なら誰でもキーアクセスに使用できます。

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
113

答えはノーだ"。しかし、あなたはこのようなプロパティへのプライベートアクセスを作成することができます:

  • モジュールを使用してください。 exportキーワードを使用して公開されていない限り、モジュール内のすべてのものは非公開です。
  • モジュール内部では、関数クロージャを使用します。 http://www.kirupa.com/html5/closures_in_javascript.htm

(プライバシーを保護するためにSymbolsを使用できるという提案は、以前のバージョンのES6仕様では当てはまりましたが、もはや当てはまりません: https://mail.mozilla.org/pipermail/es-discuss/2014- 1月/ 035604.html および https://stackoverflow.com/a/22280202/1282216 記号およびプライバシーに関する詳細な説明については、 https://curiosity-driven.org/private-properties-を参照してください。 in-javascript

29
d13

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

将来的に他の人を参照するために、個人データを保持するために 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}`)
      }
    }
  }
}
21
user1076821

に依存しますあなたが求める相手 :-)

最小限必要なクラス proposal にはprivateプロパティ修飾子は含まれていません。これは 現在のドラフト になっているようです。

しかし、 private names のサポートがあるかもしれません。これはprivateプロパティを許可します - そしてそれらはおそらくクラス定義でも使用できます。

11
Bergi

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
9
Johnny Oshika

@ 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

私が考えることができる利点:

  • プライベートメソッドを持つことができます(参照をexportしない限り、_greet_updateMessageはプライベートメソッドのように動作します)。
  • プロトタイプには含まれていませんが、インスタンスはクラスの外で(コンストラクターで定義するのではなく)一度作成されるため、上記のメソッドはメモリを節約します。
  • 私たちはモジュールの中にいるので私たちはグローバルをリークしません
  • バインドされた_privatesオブジェクトを使ってプライベートプロパティを持つこともできます

考えられるいくつかの欠点:

実行中のスニペットはここで見つけることができます: http://www.webpackbin.com/NJgI5J8lZ

9
efidiles

はい - カプセル化されたプロパティ を作成できますが、少なくともES6ではなく、アクセス修飾子(public | private)では行われていません。

ES6でそれを行うことができる方法の簡単な例を示します。

1 class Wordを使用してクラスを作成します。

2その内側のコンストラクタは、 let OR const reserved words - >を使ってブロックスコープの変数を宣言します。これらはブロックスコープなので、外部からアクセスすることはできません(カプセル化)

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
8
Nikita Kurtin

「プライベート」への異なるアプローチ

プライベート可視性がES6では現在利用できないという事実と闘う代わりに、私はあなたのIDEがJSDocをサポートするならばうまくいくもっと実用的なアプローチをとることに決めました(例えばWebstorm)。アイデアは @privateタグ を使うことです。開発に関する限り、IDEを使用すると、そのクラス外からプライベートメンバーにアクセスすることはできなくなります。私にとっては非常にうまく機能し、内部メソッドを非表示にするのに非常に便利でした。そのため、オートコンプリート機能は、クラスが実際に公開することを意味しているものだけを示しています。これが例です:

auto-complete showing just public stuff

7
Lucio Paiva

WeakMap

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

注: class はIE11ではサポートされていませんが、この例ではきれいに見えます。

6
kevlened

Benjamin's answer は、言語が明示的にプライベート変数をネイティブにサポートするまで、おそらくほとんどの場合に最適です。

しかし、何らかの理由で Object.getOwnPropertySymbols() によるアクセスを防止する必要がある場合、私が使用することを検討している方法は、プロパティ識別子として使用できる一意の、設定不可、列挙不可、書き込み不可のプロパティを付加することです。 (Symbolのような他のユニークなプロパティをまだ持っていない場合は、ユニークなidのような)構築中の各オブジェクトに対して。それから、その識別子を使って各オブジェクトの 'private'変数のマップを保存してください。

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 を使用することに対するこのアプローチの潜在的な利点は、 より速いアクセス時間 /パフォーマンスが問題になる場合です。

4
NanoWizard

個人的には、 bind演算子::の提案が好きで、それを前述の解決策@ d13と組み合わせることにしますが、今のところはクラスにexportキーワードを使用し、プライベート関数をモジュール.

ここでは触れられていない、もっと機能的なアプローチで、クラス内にすべてのプライベートな小道具やメソッドを含めることができるようになる、もう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

それについてのコメントは大歓迎です。

4
Robin F.

コンストラクタ内でクロージャを使用して「両方の長所を生かす」ことが可能であると思います。 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);
   }
};
4
JSInitiate

「クラスのプライベートデータ」のベストプラクティスを探すときに、この記事に出会いました。いくつかのパターンはパフォーマンス上の問題があるだろうと述べました。

オンラインブック「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
MarkM

実際にはシンボルとプロキシを使用することは可能です。クラススコープでシンボルを使用し、プロキシで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))はこれらのオブジェクトに対するトラップが必要な理由です。

4
Francisco Neto

おお、それほどたくさんのエキゾチックな解決策!私は通常プライバシーを気にしないので、 "擬似プライバシー" を使用します - ここで言った 。しかし、気をつけて(特別な要件がある場合)、この例のように使用します。

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)
}
3
Sergey
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"
3
Ilya Zarembsky

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

だから彼らの "プライベート"キーワードは無効です。

3
Michael Franzl

このパーティーに非常に遅く来ますが、私は検索でOPの質問にぶつかったので... はい、あなたはクロージャーでクラス宣言をラッピングすることによって私有財産を持つことができます

このcodepen に私がどのようにプライベートメソッドを持っているかの例があります。以下のスニペットでは、Subscribableクラスには2つのプライベート関数processprocessCallbacksがあります。この方法で任意のプロパティを追加することができ、それらはクロージャの使用を通じて非公開に保たれます。懸念が十分に分離されていて、クロージャがきちんと仕事をしているときに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」(または同様のもの)を使用する必要があることです。

3
Paul Whipp

WeakMapを使用してクラスにプライベートメソッドを持つことは可能です。

MDNのWebドキュメントによると

WeakMapオブジェクトは、キーがオブジェクトのみで、値が任意の値になることができるキーと値のペアのコレクションです。

キー内のオブジェクト参照は弱く保持されています。つまり、他にオブジェクトへの参照がない場合は、それらはガベージコレクション(GC)のターゲットです。

そしてこれは、配列を保持するプライベートメンバ_itemsを使って Queueデータ構造 を作成する例です。

const _items = new WeakMap();

class Queue {    
    constructor() {
        _items.set(this, []);
    }

    enqueue( item) {
        _items.get(this).Push(item);
    }    

    get count() {
        return _items.get(this).length;        
    }

    peek() {
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray[0];
    }

    dequeue() {        
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray.splice(0, 1)[0];
    }    
}

使用例

const c = new Queue();
c.enqueue("one");
c.enqueue("two");
c.enqueue("three");
c.enqueue("four");
c.enqueue("five");
console.log(c);

プライベートメンバ_itemsは隠されており、Queueオブジェクトのプロパティまたはメソッドには表示されません。

enter image description here

ただし、Queueオブジェクト内のプライベートメンバー_itemsには、次の方法でアクセスできます。

const anArray = _items.get(this);
2
StepUp

私は非常に単純な解決策を見つけました。ただ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
2
Nikola Andreev

この答え を参照してください。プライベートとパブリックのインターフェースを備えたクリーンでシンプルな「クラス」のソリューションと、構成のサポート

2
kofifus

あなたはこれを試すことができます 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;
2
João Henrique

はい、完全にできます。そして、とても簡単です。これは、コンストラクタにプロトタイプオブジェクトグラフを返すことによってプライベート変数と関数を公開することによって行われます。これは目新しいことではありませんが、その優雅さを理解するために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)); //2
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error
2
1-14x0r

私はこのパターンを使います、そしてそれはいつも私のために働きました

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
Yami Teru

実際には可能です。
1。まず、クラスを作成し、コンストラクター内で呼び出された_public関数を返します。
2。呼び出された_public関数にthis reference (すべてのプライベートメソッドと小道具へのアクセスを取得するため)、およびconstructorからのすべての引数(new Names()に渡される)
3。 _public関数スコープには、プライベートNamesクラスのthis(_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
Paweł

最後の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
1
Jayesbe

ここで、 myThing 変数は非公開で、クロージャの一部です。

class Person {
  constructor() {

    var myThing = "Hello World";

    return {
      thing: myThing,
      sayThing: this.sayThing
    }
  }

  sayThing() {
    console.log(this.thing);
  }
}

var person = new Person();

console.log(person);

ほとんどの回答では、不可能だと言っているか、または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

このメソッドを アクセサパターン と呼びます。基本的な考え方は、クロージャの内側にクロージャキーがあり、アクセスできるだけであるプライベートオブジェクト(コンストラクタ内)を作成することです。 keyがある場合.

興味があるなら、 my article でこれについてもっと読むことができます。このメソッドを使用すると、クロージャの外側からアクセスできないオブジェクトごとのプロパティを作成できます。したがって、それらをコンストラクターまたはプロトタイプで使用できますが、それ以外の場所では使用できません。私はこの方法がどこでも使われているのを見たことがないが、それは本当に強力だと思う。

1
guitarino

私たちが知っているように、ES6クラスを使ったプライベートプロパティのネイティブサポートはありません。

以下は私が使っているものです(役に立つかもしれません)。基本的に私は工場内のクラスを包んでいます。

function Animal(name) {
    const privateData = 'NO experiments on animals have been done!';

    class Animal {
        constructor(_name) {
            this.name = _name;
        }
        getName() {
            return this.name
        }
        getDisclamer() {
            return `${privateData} Including ${this.name}`
        }
    }
    return new Animal(name)
}

私は初心者なので、これが悪い方法であればとてもうれしいです。

0

前の答えを読むと、この例は上記の解決策を要約できると思いました

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

私はあなたがCapsulableと呼ばれるJavaScriptクラスでアクセス制限を使うのを助けるモジュールを開発しました。 (プライベート&保護スタティック)

興味のある方は、以下の私のパッケージをチェックしてください。 https://github.com/hmmhmmhm/capsulable

const Capsulable = require('capsulable')
const Field = Capsulable()

class A {
    constructor(_field){
        // Configure data fields.
        Field(this, _field)

        // The code below provides access to
        // the data fields when creating
        // functions within the class.
        Field(this).private
        Field(this).protected
        Field(this).protectedStatic
    }
}

module.exports = A
0
hmmhmmhm