84

Does somebody know how to make private, non-static members in CoffeeScript? Currently I'm doing this, which just uses a public variable starting with an underscore to clarify that it shouldn't be used outside of the class:

class Thing extends EventEmitter
  constructor: (@_name) ->

  getName: -> @_name

Putting the variable in the class makes it a static member, but how can I make it non-static? Is it even possible without getting "fancy"?

Nathan Arthur
  • 8,287
  • 7
  • 55
  • 80
thejh
  • 44,854
  • 16
  • 96
  • 107

11 Answers11

203

classes are just functions so they create scopes. everything defined inside this scope won't be visible from the outside.

class Foo
  # this will be our private method. it is invisible
  # outside of the current scope
  foo = -> "foo"

  # this will be our public method.
  # note that it is defined with ':' and not '='
  # '=' creates a *local* variable
  # : adds a property to the class prototype
  bar: -> foo()

c = new Foo

# this will return "foo"
c.bar()

# this will crash
c.foo

coffeescript compiles this into the following:

(function() {
  var Foo, c;

  Foo = (function() {
    var foo;

    function Foo() {}

    foo = function() {
      return "foo";
    };

    Foo.prototype.bar = function() {
      return foo();
    };

    return Foo;

  })();

  c = new Foo;

  c.bar();

  c.foo();

}).call(this);
steveluscher
  • 4,144
  • 24
  • 42
Vitaly Kushner
  • 9,247
  • 8
  • 33
  • 41
  • thx, this is good explanation but there is a typo, should be c = new Foo – qmmr May 26 '12 at 16:53
  • 9
    It should be noted that these private variables are **not** available to subclasses. – Ceasar Jun 12 '12 at 19:25
  • 45
    It should also be noted that 'private' methods will need to be called like `foo.call(this)` in order for `this` to be the function's instance. This is why trying to emulate classical inheritance in JavaScript gets hairy. – Jon Wingfield Aug 22 '12 at 14:06
  • 3
    Another downside is that you won't have access to "private" methods for unit testing.. – nuc Sep 04 '12 at 12:17
  • 16
    @nuc private methods are implementation details that get tested via the public methods that call them, which is to say that private methods shouldn't be unit tested. If a private method seems like it should be unit testable, then perhaps it should be a public method. See this post for a good explanation as well http://stackoverflow.com/questions/5750279/how-to-unit-test-private-methods-in-jquery-plugins – mkelley33 Nov 23 '12 at 06:03
  • 1
    @CeasarBautista, well, we weren't looking for *protected* variables ;) – Martin Ender Jul 05 '13 at 23:41
  • 2
    It should also be noted that you'll need to define your "private" variables above where they are used in "public" functions. Otherwise, CoffeeScript will get confused and create new internal `var` declarations which will shadow them. – Andrew Miner Apr 09 '14 at 19:26
  • This is **not** a correct answer, `foo` is a private **static** member. – Alexander Gonchiy May 22 '16 at 11:40
20

Is it even possible without getting "fancy"?

Sad to say, you'd have to be fancy.

class Thing extends EventEmitter
  constructor: (name) ->
    @getName = -> name

Remember, "It's just JavaScript."

matyr
  • 5,774
  • 28
  • 22
  • 1
    ...and so you have to do it as you would do it in JS. Easy to forget it when it's hidden behind all that sugar, thanks! – thejh Jan 14 '11 at 18:01
  • 4
    Is that really private? You can still access it outside the class. a=Thing('a') then a.getName() returns the value and a.getName = ->'b' sets it. – Amir Jan 25 '11 at 04:05
  • `getName` isn't private. `name` is. – matyr Jan 25 '11 at 14:21
  • What makes `name` private? Can you give an example of what restriction this code imposes? – Amir Jan 28 '11 at 09:24
  • 4
    @Amir: `name` is only visible from inside the constructor closure. Look at this gist: https://gist.github.com/803810 – thejh Jan 31 '11 at 09:15
  • Ok, makes sense. All member function definitions in `Thing` that use private variables need to be defined inside `constructor()`. I simply thought you were exposing `name` through `getName` for the other functions to use. – Amir Feb 02 '11 at 22:40
  • I think a better word for this type of solution would be `read-only`, not private. – Kendall Hopkins Aug 26 '11 at 19:17
  • 13
    Also worth noting that `@getName = -> name` seems to break any possible inheritance of the `getName` function. – Kendall Hopkins Sep 14 '11 at 15:06
  • 12
    This answer is wrong: Here, `getName` is public, and `name` is only accessible from the constructor function, so it is not really "private" to the object. – tothemario Aug 19 '13 at 22:19
  • Take a look at the [answer just below](http://stackoverflow.com/questions/4685626/private-members-in-coffeescript#8334365). – Dave Aug 12 '14 at 11:57
11

I'd like to show something even fancier

class Thing extends EventEmitter
  constructor: ( nm) ->
    _name = nm
    Object.defineProperty @, 'name',
      get: ->
        _name
      set: (val) ->
        _name = val
      enumerable: true
      configurable: true

Now you can do

t = new Thing( 'Dropin')
#  members can be accessed like properties with the protection from getter/setter functions!
t.name = 'Dragout'  
console.log t.name
# no way to access the private member
console.log t._name
Tim Wu
  • 2,047
  • 2
  • 22
  • 32
2

There is one problem with Vitaly's answer and that is you cannot define variables that you want to be unique to the scope, if you made a private name that way and then changed it, the name value would change for every single instance of the class, so there is one way we can solve that problem

# create a function that will pretend to be our class 
MyClass = ->

    # this has created a new scope 
    # define our private varibles
    names = ['joe', 'jerry']

    # the names array will be different for every single instance of the class
    # so that solves our problem

    # define our REAL class
    class InnerMyClass 

        # test function 
        getNames: ->
            return names;

    # return new instance of our class 
    new InnerMyClass

It's not impossible to access the the names array from outside unless you use getNames

Test this out

test = new MyClass;

tempNames = test.getNames()

tempNames # is ['joe', 'jerry']

# add a new value 
tempNames.push 'john'

# now get the names again 
newNames = test.getNames();

# the value of newNames is now 
['joe', 'jerry', 'john']

# now to check a new instance has a new clean names array 
newInstance = new MyClass
newInstance.getNames() # === ['joe', 'jerry']


# test should not be affected
test.getNames() # === ['joe', 'jerry', 'john']

Compiled Javascript

var MyClass;

MyClass = function() {
  var names;
  names = ['joe', 'jerry'];
  MyClass = (function() {

    MyClass.name = 'MyClass';

    function MyClass() {}

    MyClass.prototype.getNames = function() {
      return names;
    };

    return MyClass;

  })();
  return new MyClass;
};
iConnor
  • 19,997
  • 14
  • 62
  • 97
2

Here is a solution that draws on several of the other answers here plus https://stackoverflow.com/a/7579956/1484513. It stores the private instance (non-static) variables in a private class (static) array and uses an object ID to know which element of that array contains the data belonging to each instance.

# Add IDs to classes.
(->
  i = 1
  Object.defineProperty Object.prototype, "__id", { writable:true }
  Object.defineProperty Object.prototype, "_id", { get: -> @__id ?= i++ }
)()

class MyClass
  # Private attribute storage.
  __ = []

  # Private class (static) variables.
  _a = null
  _b = null

  # Public instance attributes.
  c: null

  # Private functions.
  _getA = -> a

  # Public methods.
  getB: -> _b
  getD: -> __[@._id].d

  constructor: (a,b,@c,d) ->
    _a = a
    _b = b

    # Private instance attributes.
    __[@._id] = {d:d}

# Test

test1 = new MyClass 's', 't', 'u', 'v'
console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 t u v

test2 = new MyClass 'W', 'X', 'Y', 'Z'
console.log 'test2', test2.getB(), test2.c, test2.getD()  # test2 X Y Z

console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 X u v

console.log test1.a         # undefined
console.log test1._a        # undefined

# Test sub-classes.

class AnotherClass extends MyClass

test1 = new AnotherClass 's', 't', 'u', 'v'
console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 t u v

test2 = new AnotherClass 'W', 'X', 'Y', 'Z'
console.log 'test2', test2.getB(), test2.c, test2.getD()  # test2 X Y Z

console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 X u v

console.log test1.a         # undefined
console.log test1._a        # undefined
console.log test1.getA()    # fatal error
Community
  • 1
  • 1
Waz
  • 653
  • 6
  • 12
2

Here's the best article I found about setting public static members, private static members, public and private members, and some other related stuff. It covers much details and js vs. coffee comparison. And for the historical reasons here's the best code example from it:

# CoffeeScript

class Square

    # private static variable
    counter = 0

    # private static method
    countInstance = ->
        counter++; return

    # public static method
    @instanceCount = ->
        counter

    constructor: (side) ->

        countInstance()

        # side is already a private variable, 
        # we define a private variable `self` to avoid evil `this`

        self = this

        # private method
        logChange = ->
            console.log "Side is set to #{side}"

        # public methods
        self.setSide = (v) ->
            side = v
            logChange()

        self.area = ->
            side * side

s1 = new Square(2)
console.log s1.area()   # output 4

s2 = new Square(3)
console.log s2.area()   # output 9

s2.setSide 4            # output Side is set to 4
console.log s2.area()   # output 16

console.log Square.instanceCount() # output 2
plunntic iam
  • 964
  • 1
  • 9
  • 18
1

Here is how you can declare private, non-static members in Coffeescript
For full reference, you can take a look at https://github.com/vhmh2005/jsClass

class Class

  # private members
  # note: '=' is used to define private members
  # naming convention for private members is _camelCase

  _privateProperty = 0

  _privateMethod = (value) ->        
    _privateProperty = value
    return

  # example of _privateProperty set up in class constructor
  constructor: (privateProperty, @publicProperty) ->
    _privateProperty = privateProperty
Strong Bear
  • 411
  • 4
  • 7
1

"class" in coffee scripts leads to an prototype based result. So even if you use a private variable it is shared between instances. You can do this:

EventEmitter = ->
  privateName = ""

  setName: (name) -> privateName = name
  getName: -> privateName

.. leads to

emitter1 = new EventEmitter()
emitter1.setName 'Name1'

emitter2 = new EventEmitter()
emitter2.setName 'Name2'

console.log emitter1.getName() # 'Name1'
console.log emitter2.getName() # 'Name2'

But be careful to put the private members before the public functions, because coffee script returns the public functions as object. Look at the compiled Javascript:

EventEmitter = function() {
  var privateName = "";

  return {
    setName: function(name) {
      return privateName = name;
    },
    getName: function() {
      return privateName;
    }
  };
};
0

You can't do it easily with CoffeeScript classes, because they use the Javascript constructor pattern for creating classes.

However, you could say something like this:

callMe = (f) -> f()
extend = (a, b) -> a[m] = b[m] for m of b; a

class superclass
  constructor: (@extra) ->
  method: (x) -> alert "hello world! #{x}#{@extra}"

subclass = (args...) -> extend (new superclass args...), callMe ->
  privateVar = 1

  getter: -> privateVar
  setter: (newVal) -> privateVar = newVal
  method2: (x) -> @method "#{x} foo and "

instance = subclass 'bar'
instance.setter 123
instance2 = subclass 'baz'
instance2.setter 432

instance.method2 "#{instance.getter()} <-> #{instance2.getter()} ! also, "
alert "but: #{instance.privateVar} <-> #{instance2.privateVar}"

But you lose the greatness of CoffeeScript classes, because you can't inherit from a class created that way by any other way than by using extend() again. instanceof will stop working, and objecs created this way consume a little bit more memory. Also, you mustn't use the new and super keywords anymore.

The point is, that the closures must be created every time a class is instantiated. The member closures in pure CoffeeScript classes are created only once - that is, when the class runtime "type" is constructed.

Jaakko Salomaa
  • 106
  • 1
  • 4
0

Since coffee script compiles down to JavaScript the only way you can have private variables is through closures.

class Animal
  foo = 2 # declare it inside the class so all prototypes share it through closure
  constructor: (value) ->
      foo = value

  test: (meters) ->
    alert foo

e = new Animal(5);
e.test() # 5

This will compile down through the following JavaScript:

var Animal, e;
Animal = (function() {
  var foo; // closured by test and the constructor
  foo = 2;
  function Animal(value) {
    foo = value;
  }
  Animal.prototype.test = function(meters) {
    return alert(foo);
  };
  return Animal;
})();

e = new Animal(5);
e.test(); // 5

Of course this has all the same limitations as all the other private variables you can have through the use of closures, for example, newly added methods don't have access to them since they were not defined in the same scope.

Ivo Wetzel
  • 46,459
  • 16
  • 98
  • 112
-3

If you want only separate private memebers from public, just wrap it in $ variable

$:
        requirements:
              {}
        body: null
        definitions: null

and use @$.requirements

borovsky
  • 873
  • 1
  • 9
  • 15