web-dev-qa-db-ja.com

Traceurを使ってES6クラスにプライベートメソッドを実装する方法

私は今、ES 6の機能を活用するためにTraceur Compilerを使用しています。

私はES5からこのものを実装したいです。

function Animal() {
    var self = this,
        sayHi;

    sayHi  = function() {
        self.hi();
    };

    this.hi = function() {/* ... */}
}

現在traceurはprivatename__およびpublicname__キーワードをサポートしていません( Harmony から)。また、ES6クラス構文では、クラス本体で単純なvarname__(またはletname__)ステートメントを使用することはできません。

私が見つける唯一の方法は、クラス宣言の前にprivatesをシミュレートすることです。何かのようなもの:

var sayHi = function() {
    // ... do stuff
};

class Animal {
...

それは何よりも良いことではありませんが、期待どおりにthisname__またはapplynameなしで正しいbindname__をprivateメソッドに渡すことはできません。

それでは、traceurコンパイラと互換性のあるES6クラスでプライベートデータを使用する可能性はありますか?

129
Glen Swift

Current ECMAScript 6仕様書 には、privatepublic、またはprotectedキーワードはありません。

したがってTraceurはprivatepublicをサポートしません。 6to5(現在は "Babel"と呼ばれています)は実験的な目的で この提案 を実現しています( この説明を参照 )。しかし、それは単なる提案です。

そのため、今のところWeakMapを使ってプライベートプロパティをシミュレートすることができます( here を参照)。もう1つの選択肢はSymbolです - しかし、プロパティはObject.getOwnPropertySymbolsを通して簡単にアクセスできるので実際のプライバシーを提供しません。

私見、現時点での最良の解決策 - 疑似プライバシーを使用してください。このメソッドでapplyまたはcallを頻繁に使用する場合、このメソッドは非常にオブジェクト固有のものです。それで、アンダースコアの接頭辞を付けて、あなたのクラスでそれを宣言することは価値があります:

class Animal {

    _sayHi() {
        // do stuff
    }
}
204
alexpods

通常の機能はいつでも使用できます。

function myPrivateFunction() {
  console.log("My property: " + this.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
    myPrivateFunction.bind(this)();
  }
}

new MyClass(); // 'My property: myProp'
65
Max

現時点では、メソッドやプロパティをプライベートとして宣言する方法はありませんが、ES6モジュールはグローバル名前空間にはありません。したがって、自分のモジュールで宣言してエクスポートしないものは、プログラムの他の部分では利用できませんが、それでも利用できます。実行時にモジュール。したがって、あなたはプライベートなプロパティとメソッドを持っています:)

以下はその例です(test.jsファイル内)

function tryMe1(a) {
  console.log(a + 2);
}

var tryMe2 = 1234;

class myModule {
  tryMe3(a) {
    console.log(a + 100);
  }

  getTryMe1(a) {
    tryMe1(a);
  }

  getTryMe2() {
    return tryMe2;
  }
}

// Exports just myModule class. Not anything outside of it.
export default myModule; 

別のファイルに

import MyModule from './test';

let bar = new MyModule();

tryMe1(1); // ReferenceError: tryMe1 is not defined
tryMe2; // ReferenceError: tryMe2 is not defined
bar.tryMe1(1); // TypeError: bar.tryMe1 is not a function
bar.tryMe2; // undefined

bar.tryMe3(1); // 101
bar.getTryMe1(1); // 3
bar.getTryMe2(); // 1234
52

あなたはシンボルを使用することができます

var say = Symbol()

function Cat(){
  this[say]() // call private methos
}

Cat.prototype[say] = function(){ alert('im a private') }

P.Sアレックスポッドは正しくありません。継承は名前の衝突であるため、彼は非公開よりも保護を受けます

実際には代わりにvar say = String(Math.random())を使うことができますSymbol

ES6の場合:

var say = Symbol()

class Cat {

  constructor(){
    this[say]() // call private
  }

  [say](){
    alert('im private')
  }

}
20
Maxmaxmaximus

これが役に立つことを願っています。 :)

I. var、宣言中の関数 IIFE(即時呼び出し関数式) を宣言すると、これらは無名関数でのみ使用できます。 (ES6用にコードを変更する必要がある場合は、 'var'を使用せずに "let、const"キーワードを使用することをお勧めします。)

let Name = (function() {
  const _privateHello = function() {
  }
  class Name {
    constructor() {
    }
    publicMethod() {
      _privateHello()
    }
  }
  return Name;
})();

II。 WeakMapオブジェクトはメモリリークのトラブルに良いでしょう。

インスタンスが削除されると、WeakMapに格納されている変数は削除されます。この記事をチェックしてください。 ( ES6クラスのプライベートデータの管理

let Name = (function() {
  const _privateName = new WeakMap();
})();

III。全部まとめてみましょう。

let Name = (function() {
  const _privateName = new WeakMap();
  const _privateHello = function(fullName) {
    console.log("Hello, " + fullName);
  }

  class Name {
    constructor(firstName, lastName) {
      _privateName.set(this, {firstName: firstName, lastName: lastName});
    }
    static printName(name) {
      let privateName = _privateName.get(name);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
    printName() {
      let privateName = _privateName.get(this);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
  }

  return Name;
})();

var aMan = new Name("JH", "Son");
aMan.printName(); // "Hello, JH Son"
Name.printName(aMan); // "Hello, JH Son"
12
Son JoungHo

アレックスポッドが言うように、ES6ではこれを行うための専用の方法はありません。しかし、興味のある人のために、この種の構文を有効にする バインド演算子 の提案もあります。

function privateMethod() {
  return `Hello ${this.name}`;
}

export class Animal {
  constructor(name) {
    this.name = name;
  }
  publicMethod() {
    this::privateMethod();
  }
}

繰り返しますが、これは単なる提案です。あなたのマイレージは異なる場合があります。

9
jamsesso

ファクトリー機能の使用を検討しましたか?通常、これらは JavaScriptまたはクラスのコンストラクタ関数に代わるはるかに優れた代替手段 です。これがどのように機能するかの例は、次のとおりです。

function car () {

    var privateVariable = 4

    function privateFunction () {}

    return {

        color: 'red',

        drive: function (miles) {},

        stop: function() {}

        ....

    }

}

クロージャのおかげで、返されたオブジェクト内のすべてのプライベート関数と変数ラベルにアクセスできますが、外部からそれらにアクセスすることはできません。

5
Nicola Pedretti

Marcelo Lazaroni はすでに言ったように、

現在、メソッドやプロパティをプライベートとして宣言する方法はありませんが、ES6モジュールはグローバル名前空間にはありません。したがって、モジュール内で宣言してエクスポートしないものは、プログラムの他の部分では使用できませんが、実行時にはまだモジュールで使用できます。

しかし彼の例は、プライベートメソッドがクラスのインスタンスのメンバーにどのようにアクセスできるかを示していません。 Max は、バインディングや、コンストラクタでラムダメソッドを使用する代替方法によるインスタンスメンバーへのアクセス方法の良い例を示していますが、追加したいと思いますもっと簡単な方法は、インスタンスをパラメータとしてプライベートメソッドに渡すことです。このようにすると、MaxのMyClassは次のようになります。

function myPrivateFunction(myClass) {
  console.log("My property: " + myClass.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
  }
  testMethod() {
    myPrivateFunction(this);
  }
}
module.exports = MyClass;

どちらを選択するかは、実際には個人の好みによって決まります。

4
Patrick Graham

私は次のことを可能にするはるかに優れた解決策であると感じるものを思いつきました。

  • 'this._'、その/ self、weakmaps、シンボルなどは必要ありません。明確でわかりやすい 'class'コード

  • プライベート変数とメソッドは本当にプライベートで、正しい 'this'バインディングを持ちます

  • 'this'はまったく使用されていません。これは、エラーが発生しにくい、明確なコードを意味します。

  • パブリックインタフェースは明確で、プライベートメソッドへのプロキシとしての実装からは分離されています

  • 簡単な合成が可能

これであなたはできる:

function Counter() {
  // public interface
  const proxy = {
    advance,  // advance counter and get new value
    reset,    // reset value
    value     // get value
  }
        
  // private variables and methods
  let count=0;
    
  function advance() {
    return ++count;
  }
        
  function reset(newCount) {
    count=(newCount || 0);
  }
        
  function value() {
    return count;
  }
    
  return proxy;
}
        
let counter=Counter.New();
console.log(counter instanceof Counter); // true
counter.reset(100);
console.log('Counter next = '+counter.advance()); // 101
console.log(Object.getOwnPropertyNames(counter)); // ["advance", "reset", "value"]
<script src="https://cdn.rawgit.com/kofifus/New/7987670c/new.js"></script>

コードと、コンストラクタや合成を含むより複雑な例については、 新しい を参照してください。

2
kofifus