2

Coming from a C++/Java background, OOP-Inheritance in CoffeeScript got me confused.

Consider the following example:

class BaseClass
    arr: []

    addItem: (item) =>
        @arr.push item

class SubClassA extends BaseClass
    constructor: ->
        @addItem("fromSubA")

class SubClassB extends BaseClass


console.log "Case 1"
instB = new BaseClass()
instA = new SubClassA()
console.log "instA: #{JSON.stringify instA.arr}"
console.log "instB #{JSON.stringify instB.arr}"

console.log "Case 2"
instB = new SubClassB()
instA = new SubClassA()
console.log "instA: #{JSON.stringify instA.arr}"
console.log "instB #{JSON.stringify instB.arr}"

console.log "Case 3"
instB = new SubClassB()
instA = new SubClassA()
console.log "instA: #{JSON.stringify instA.arr}"
console.log "instB #{JSON.stringify instB.arr}"

Output in tinkerbin.com:

Case 1
instA: ["fromSubA"] 
instB ["fromSubA"] 

Case 2
instA: ["fromSubA","fromSubA"] 
instB ["fromSubA","fromSubA"] 

Case 3
instA: ["fromSubA","fromSubA","fromSubA","Added manually, only to instB"] 
instB ["fromSubA","fromSubA","fromSubA","Added manually, only to instB"] 

Iow: the 'arr' instance-property of base-class behaves more or less like it's a static property; If I change the array in one instance, it's also changed in the other instance. Why is this array shared between instances?

Confusingly, a string property doesn't exhibit this behavior:

class BaseClass
    property: "From Base"    

class SubClassA extends BaseClass
    constructor: ->
        @property = "fromSubClassA"

class SubClassB extends BaseClass


document.write "Case 1<br>"
instB = new BaseClass()
instA = new SubClassA()
document.write "#{instA.property} <br>"
document.write "#{instB.property} <br><br>"

document.write "Case 2<br>"
instB = new SubClassB()
instA = new SubClassA()
instA.property = "test"
document.write "#{instA.property} <br>"
document.write "#{instB.property} <br>"

This code works just fine, keeping the 'property' property isolated between instances.

I'm clearly misunderstanding something here. What causes the array to become shared between instances?

TinkerTank
  • 5,685
  • 2
  • 32
  • 41

2 Answers2

2

Take a look at what the generated JavaScript looks like:

var BaseClass,
  __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

BaseClass = (function() {
  function BaseClass() {
    this.addItem = __bind(this.addItem, this);
  }

  BaseClass.prototype.arr = [];

  return BaseClass;

})();

As you can see, arr is a property of the prototype of BaseClass, just like a class variable in languages like Python. Only one instance of that array exists and is shared among all instances of that class, so if you modify it, it's modified everywhere (by accessing instance.arr, you're traversing up the prototype chain up to that same BaseClass.prototype.arr array).

Strings are immutable in JavaScript, so they don't behave like arrays in that regard.

Blender
  • 289,723
  • 53
  • 439
  • 496
  • Right, basically I should remember that any properties that are defined in this way in the class, are going to be attached to the prototype of the class, and are actually shared between all instances of that class and subclasses. Thanks! – TinkerTank Jun 12 '13 at 15:56
2

In your example arr is a class (i.e. static) property variable, so it is shared with all instances of your BaseClass.

You need to edit your BaseClass and initialize the arr property in the constructor method:

class BaseClass
    constructor: ->
        @arr = []

    addItem: (item) =>
        @arr.push item

And then you want to make sure that SubClassA is calling the BaseClass costructor using the super() keyword:

class SubClassA extends BaseClass
    constructor: ->
        super()
        @addItem("fromSubA")
Paolo Moretti
  • 54,162
  • 23
  • 101
  • 92
  • Yes! - I understood this based on Blenders' answer, but for future reference it's nice to have both the problem and the solution together. Thanks! – TinkerTank Jun 12 '13 at 16:05
  • "in your example arr is a class (i.e. static) property, so it is shared with all instances of your BaseClass." - that's not the definition of a static property. Static methods/properties (i.e. those that would be accessible through `BaseClass.propertyName` are created with `@propertyName: value` assignments in the body of the class definition body. The suggested remedy is correct though. – Myrne Stol Jun 12 '13 at 16:46
  • Paolo, I don't think it's a proper fix. The word "static" just does not apply. If anything, it's a "shared" property. Normal OO languages don't have such a concept. Something either appears on a object, or as a static property, on the class itself. Not somewhere in between. – Myrne Stol Jun 12 '13 at 18:26
  • @MerynStol: Python (which is OO) has class variables, which behave exactly like this. They're bound to the class, not to an instance of the class. Besides, JavaScript uses prototypical inheritance, so not all object-oriented concepts apply. – Blender Jun 12 '13 at 18:31
  • I see in http://stackoverflow.com/questions/68645/static-class-variables-in-python that Python indeed has indeed a notion of static variables which can be accessed via a class instance. Maybe state that you mean static variables as seen in Python? I never heard of this language feature before. There's actually *still* a difference because while this property is available through multiple instances, it's not accessible as a member of the class variable itself (`BaseClass.arr`) won't work. For that, in case of CoffeeScript, you really need its "static properties", with `@` inside the class body. – Myrne Stol Jun 12 '13 at 19:18