18

I have the following CoffeeScript code:

class Person
  secret = 0
  constructor: (@name, @age, @alive) ->
  inc: -> secret++

Which compiles to the following JavaScript code:

var Person;
Person = (function() {   
    var secret;
    secret = 0;

    function Person(name, age, alive) {
        this.name = name;
        this.age = age;
        this.alive = alive;
    }
    Person.prototype.inc = function() {
        return secret++;
    };
    return Person;
})();

Currently secret is shared between all instances of Person. Is there a way to make secret a private instance variable in CoffeeScript?

knpwrs
  • 15,691
  • 12
  • 62
  • 103

9 Answers9

18

I figured out a solution. I am not sure if this is the best solution, so I am still open to others.

CoffeeScript:

class Person
  constructor: (@name, @age, @alive) ->
    secret = 0
    @inc = -> secret++;

JavaScript:

var Person;
Person = (function() {
    function Person(name, age, alive) {
        var secret;
        this.name = name;
        this.age = age;
        this.alive = alive;
        secret = 0;
        this.inc = function() {
            return secret++;
        };
    }
    return Person;
})();
knpwrs
  • 15,691
  • 12
  • 62
  • 103
16

There is no concept of private members in CoffeeScript as there is none in JavaScript. There are local variables, which you do utilize well in your own solution, but while your solution does hide the secret variable from anything outside of a constructor function, it also introduces an overhead of redeclaring the inc method on every instantiation of a class Person.

A mistake very common in JavaScript community is trying to project inexistent features of other languages on it, which an attempt of mimicing private members is obviously a case of. There's no such concept in it and thinking deeper you'll conclude that it would be just unnatural to an extremely loose dynamic environment which JavaScript is.

So don't waste your time and performance of your application on implementing inexistent constructs. Just concentrate on solving your problem - not the problems of lacking language features.

Now ask yourself: what is so hurtful in having all members public?

Taking everything said into account the ultimate solution would be:

class Person
  constructor: (@name, @age, @alive) ->
    @secret = 0
  inc: -> @secret++
Community
  • 1
  • 1
Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169
  • 4
    It's common to use naming conventions to denote "private" variables. Similarly to Python, this is usually denoted by `_var`, i.e. `_secret` in this case. – Aaron Dufour Mar 27 '12 at 16:14
  • 1
    @AaronDufour What you suggest is another attempt on mimicking the private members feature. My point is that JavaScript developer has to free his mind from such a concept, because in the end all solutions like that end up to be an overcomplication without any practical purpose. – Nikita Volkov Mar 27 '12 at 17:31
  • 21
    It doesn't mimic private members; it just makes it more clear how the class is intended to be used. Calling the variable `secret` probably is enough, but the underscore prefix is a more general solution. This is particularly useful if the class is being exposed to third parties. – Aaron Dufour Mar 27 '12 at 18:15
  • @NikitaVolkov you are right in principle; i used to do a lot of this 'working against the grain' stuff; it gives you * better language understanding, * little gain for the software, * knowledge that time can be spent better.—apart from that, JS is great when it comes to closures, and KPthunder's solution, below, goes in that direction: variables in a closure can be both private and particular to the instance. – flow Jun 17 '14 at 19:51
  • Hurtful is when you define classes in coffee using extends. Without the private infrastructure you can unintentionally shadow a "private" member of the base class. So your solution is just almost the ultimate one. – Jamesgt Nov 04 '14 at 13:59
  • 1
    javascript absolutely has a concept of private variables. they are often implemented with closures, but simply not assigning a variable inside a function to `this` makes it private by default. just because js is implicit about privacy for variables (ie. there is no `public`/`private` keyword), doesn't mean it doesn't support or encourage it. – bcherny Aug 20 '15 at 18:50
  • @bcherny Those are local variables, which I mention in the first paragraph. – Nikita Volkov Aug 21 '15 at 10:47
2

While it won't truly hide them, the convention is to prefix "private" members with an underscore. The idea here is that folks consuming this should assume that such members are implementation details and are advised not to use them.

Larry Maccherone
  • 9,393
  • 3
  • 27
  • 43
1

Here is a trick that will fulfill the requirement most of the time:

msg = "Result-----------\n"

class Dog

  #public variable
  dogName: null

  #private variables
  _private = []

  constructor: (@dogName, surname) ->
    _private[@dogName] = {surname: surname}

  #public method (note the => instead of ->)
  walk: =>
    msg += "#{@dogName} is walking\n"
    sayHello(@dogName, _private[@dogName].surname)

  #private method
  sayHello = (dog, surname) ->
    msg += "Hi! says #{dog}. Call me #{surname}\n"

window.ralph = new Dog("Ralph", "Ralphie")
window.peter = new Dog("Peter", "Pitty")
window.rex = new Dog("Rex", "Roxie")
rex.walk()
peter.walk()
ralph.walk()

alert msg

but you have to keep in mind that : _private grows with each new instance (it is a shared array) and your key (in this case @dogName) has to be unique accross instances.

Try it

xpou
  • 101
  • 1
  • 4
1

Refer to this thread, you could only make it work through closured getter function

Community
  • 1
  • 1
otakustay
  • 11,817
  • 4
  • 39
  • 43
0

You are actually hiding implementation details by declaring a member private. By using the inheritance you can hide implementation details, by declaring an 'abstract' class (which actually doesn't exist in CoffeeScript, as far as I know) and extending it with your own implementation. Like so:

  class bottle
        drink: ->
        empty: ->

  class bottle_impl extends bottle
        drink: -> 
             if count > 0
                 alert "drinking for the " + _count + " time."
       empty: -> @_count = 0
       _count: 4

now every function that returns a bottle should actually return a bottle_impl (while hiding that fact from the user in the comments). Unfortunatly, when extending a bottle, you won't extend a bottle_impl which is probably not what you want.

user23127
  • 827
  • 10
  • 21
0

hmmmmmmmm not completely true. if you want private members you can't really get private members but if you really really want it you can have it

class person 
  constructor:->
    name='my secret name'
    @getName=->
      name
    null


boy= new person()


alert boy.name  #it's now private spits out undefined
alert boy.getName()

they need to be in the scope of the construct inorder for this to work. it's as close to private as you will ever get.

Problems private memebers will slow down the application. It needs to run everytime instanced and it can't just get it from the prototype. Have fun

Lpc_dark
  • 2,834
  • 7
  • 32
  • 49
  • proof in the pudding http://coffeescript.org/#try:%20class%20person%20%0A%20%20%20%20%20%20constructor%3A-%3E%0A%20%20%20%20%20%20%20%20name%3D'my%20secret%20name'%0A%20%20%20%20%20%20%20%20%40getName%3D-%3E%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20null%0A%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20boy%3D%20new%20person()%0A%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20alert%20boy.name%20%20%23it's%20now%20private%20spits%20out%20undefined%0A%20%20%20%20alert%20boy.getName() – Lpc_dark Mar 29 '14 at 20:06
0

You could do something like this...

class person
  secret = 
    name : "my secret name"
  constructor:->
    x = 123
  getName: ->
    secret.name

user = new person()

console.log user.name # undefined
console.log user.getName() # "my secret name"

It is really private the downside is you will have to use a variable secret or _ or whatever you choose to reference the private variables.

Val
  • 17,336
  • 23
  • 95
  • 144
0

Although Javascript does not have visibility concept, you can use scopes to emulate that.

class Test
  privateVariable = null

  privateFunction = (obj)->
    privateVariable = obj
  constructor: (a) ->
    privateVariable = a

  publicFunction: ->
    return privateVariable

  setPrivateVariable: (obj) ->
    privateFunction(obj)

test = new Test("Test")
console.log(test.privateVariable)   # undefined
console.log(test.publicFunction())  # Test
test.setPrivateVariable("Changed!") #
console.log(test.publicFunction())  # Changed!

It is a very debatable subject. I personally like to hide some properties and methods to make the class' contract clearer.

Hadrien
  • 49
  • 3