CoffeeScriptでプライベートなクラス変数を定義する方法


※ 今回のやり方は、インスタンス変数ではなくクラス変数での話です。複数のインスタンスで、1つのクラス変数が共有されてしまいます。インスタンス変数をプライベートにする方法はないようです。
@kaminalyさん、ありがとうございます。

CoffeeScriptのプライベート変数は、コロンではなくイコールで

下記は、人間に自己紹介させるクラスです。_nameはプライベート変数にしてあります。CoffeeScriptでは、プライベート変数はクラス定義の直下に書きます。プラベートメソッドの場合は、変数の値の代わりに関数を指定すればいいです。とにかくプライベートの場合は、コロンではなくイコールを使って定義しましょう。class内で、コロンを使うと、メソッドになってしまいます。プライベートメソッドの場合は、イコールを使うところに注意です。

class Human
  _name = ''
  constructor: (name) ->
    _name = name
  say: ->
    console.log("I'm #{_name}")

takeshi = new Human("takeshi")
takeshi.say() # I'm takeshi
console.log(takeshi._name) # undefined

CoffeeScriptでは、変数宣言ができない

CoffeeScriptには、varがないので変数の宣言のみを記述することができません。なので、今回は初期値として_nameには空文字を入れています。空文字も嫌な人は、undefinedを初期値として代入してあげればいいです。

JavaScriptでは、クロージャでプライベート変数を作る

下記は、上記のcoffeeファイルを、コンパイルして整形したjsファイルです。Humanという変数には、無名関数の中によって定義されたHuman関数が戻り値として代入されています。JavaScriptでクラスを擬似的に表現するのに、関数を使います。

// Generated by CoffeeScript 1.10.0
(function() {
  var Human, takeshi;

  Human = (function() {
    var _name;

    _name = '';

    function Human(name) {
      _name = name;
    }

    Human.prototype.say = function() {
      return console.log("I'm " + _name);
    };

    return Human;

  })();

  takeshi = new Human("takeshi");

  takeshi.say();

  console.log(takeshi._name);

}).call(this);

このHuman関数はコンストラクタになります。この関数の中で参照している_name変数は、Human関数の外側で定義したものです。このように自分の関数定義より、外側のコンテキストから変数を参照すると、内側の関数を実行しても、変更した内容が保持されます。これがクロージャです。

thisでインスタンスとバインドしないから、プライベート変数になる

もし、Human関数のコンストラクタで_nameではなく、this._nameとしてしていたら、このthisはHuman関数によって作成されたインスタンスを指します。このようにthisのプロパティとして変数を紐付けることをバインドと言います。

Human関数で生成されたインスタンスを通してアクセスできるのは、このようにthisでバインドされたものです。_namethisでインスタンスにくっつけてたりせず、全然関係ないところで定義されたものを使っています。だから、Humanクラスのインスタンスを通してアクセスすることが出来ないのです。結果として、プライベート変数になります。