Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoffeeScript Encapsulation and Variable Access

Tags:

coffeescript

Trying to understand how CoffeeScript instance and class variable works I came with this code (and the results are in the comments).

class A
  x: 1
  @y: 2

  constructor: (@z) -> 
    #console.log "const x", x #ReferenceError: x is not defined
    console.log "constructor y", @y #undefined
    console.log "constructor z", @z # = 3 for A and 6 for B

  get: () -> 
    #console.log "get x", x #ReferenceError: x is not defined
    console.log "get y", @y #undefined
    console.log "get z", @z # = 3 for A and 6 for B

  get2: () => 
    #console.log "get2 x", x #ReferenceError: x is not defined
    console.log "get2 y", @y #undefined
    console.log "get2 z", @z # = 3 for A and 6 for B

  @get3: () -> 
    #console.log "get3 x", x #ReferenceError: x is not defined
    console.log "get3 y", @y # = 2
    console.log "get3 z", @z #undefined

  @get4: () => 
    #console.log "get4 x", x #ReferenceError: x is not defined
    console.log "get4 y", @y # = 2
    console.log "get4 z", @z #undefined

class B extends A
  constructor: (@w) ->
    super(@w)

console.log '------A------'
i = new A 3
console.log "i.x", i.x # = 1
console.log "i.y", i.y #undefined
console.log "i.z", i.z # = 6
i.get()
i.get2()
A.get3()
A.get4()
console.log '------B------'
i = new B 6
console.log "i.x", i.x # = 1
console.log "i.y", i.y #undefined
console.log "i.z", i.z # = 6
console.log "i.w", i.w # = 6
i.get()
i.get2()
B.get3()
B.get4()
console.log '------------'

There are some strange things happening here:

  1. x var I was expecting to access it from any method but x var can't be accessed from any method or constructor (ReferenceError). I'm only able to access it from a instance of A or B (i.x). Why is that?

  2. @y var I was expecting to get @y var value from any method but it has no value in most of places (undefined value, not a ReferenceError exception). @y has value only on @get3 and @get4 (instance methods?). If it is defined, why I can't get its value?

  3. @y and @z var Both @y and @z are instance variables, but because @z was initialized in the constructor, it has a differentiated behavior. @y is valid on @get3 and @get4 and @z is valid on get and get2. Again, what is happening here?

The thing is that I'm really confused by these behaviors. Is this code correct? So, should I learn more about JS generated by CS?

Tks

like image 288
David Lojudice Sb. Avatar asked Jul 01 '11 18:07

David Lojudice Sb.


2 Answers

In function bodies, @ refers to this and in class definitions, @ refers to the class itself rather than the prototype.

So in the example above, the definition of @y refers to A.y, and not A.prototype.y. It's tricky to refer to it because of the way this is bound in the various ways of defining methods. You can access it using @y from methods named @get because in this case this always refers to A.

The definition of x refers to A.prototype.x and so from your get methods you should access it via @x in get1 and get2.

As a basic guide, try not to use @ outside of function bodies and everything will make a lot more sense:

class A
  constructor: (@a) ->
  b: 2
  tryStuff: =>
    console.log(@a) #will log whatever you initialized in the constructor
    console.log(@b) #will log 2

EDIT: you could consider methods defined as @something to be static methods of that class, so you can call them with classname.something() but as static methods, they can't access any instance variables.

like image 190
nicolaskruchten Avatar answered Sep 29 '22 15:09

nicolaskruchten


To respond to your questions:

  1. x = 1 within the class body would create a variable named x within the scope body. But x: 1 within the class body defines a property x on the prototype. If you change console.log x to console.log @x, then you'll get 1.
  2. Within the class body, @ points to the class itself. Within the constructor (and in methods called as instance.method), it points to the particular instance. Change console.log @y to console.log A.y, and you'll get 2. (You can also use @constructor to get a reference to the class from the instance, because in JavaScript, the class actually is the constructor function.)
  3. Since @ in the constructor points to the instance, you're setting the instance's z property to the given value.

And yes, I do recommend understanding the underlying JavaScript—I know it's a bit odd for @ to have so many different meanings, but it makes a lot of sense once you understand JavaScript's this (one of the trickier parts of the language, to be sure). Incidentally, my book has a lot more info on this.

like image 36
Trevor Burnham Avatar answered Sep 29 '22 16:09

Trevor Burnham