class Ninja constructor: (@numOfShurikens) -> throwShuriken: -> @numOfShurikens-- class Ronin extends Ninja constructor: (numOfShurikens) -> super numOfShurikens+1 # ronins know to carry a spareSubclassing in JavaScript is a bit tedious and CoffeeScript helps. The above code translates to the following JavaScript.
var Ninja, Ronin; var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) { child[key] = parent[key]; } } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Ninja = (function() { function Ninja(numOfShurikens) { this.numOfShurikens = numOfShurikens; } Ninja.prototype.throwShuriken = function() { return this.numOfShurikens--; }; return Ninja; })(); Ronin = (function() { __extends(Ronin, Ninja); function Ronin(numOfShurikens) { Ronin.__super__.constructor.call(this, numOfShurikens + 1); } return Ronin; })();
var Ninja, Ronin;Declaring the variables now and assigning them later makes hoisting [3] explicit. I don’t see an advantage to declaring the two variables later, when each of the assignments is made.
var __hasProp = Object.prototype.hasOwnProperty,Shortcut for hasOwnProperty, slightly increases performance, because dereferencing happens less often. On the other hand, it feels unnecessary to waste a global variable for this, putting it directly in front of the for loop would probably have been enough.
__extends = function(child, parent) { // (1) for (var key in parent) { if (__hasProp.call(parent, key)) { child[key] = parent[key]; } } // (2) function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; // (3) child.__super__ = parent.prototype; return child; };Remember that a constructor function C loosely corresponds to a class in that it carries the name of the type and that it is used to produce new instances. However, the instance methods (which are shared by all instances) are put into C.prototype. When you subclass, you thus have to consider two levels: On one hand class methods C.classMethod(), on the other hand instance methods C.prototype.instanceMethod(). Beware that the prototype property of C only has hat name because it will become the prototype of objects that are created via new C(). Thus, C.prototype is not the prototype of the function C, but rather a property of the function object. Steps performed above:
child.prototype = Object.create(parent.prototype); child.prototype.constructor = child;The constructor property [2] is correctly set up in the default prototype. As we assign our own prototype object, we need to set up that property manually.
Ninja = (function() { function Ninja(numOfShurikens) { this.numOfShurikens = numOfShurikens; } Ninja.prototype.throwShuriken = function() { return this.numOfShurikens--; }; return Ninja; })();A normal JavaScript class definition, wrapped in an IIFE [3]. There is no benefit for using an IIFE here, apart from having a single assignment (which would matter in an object literal, but doesn’t here). Note that the identifier Ninja in the first line is a different variable than the identifier Ninja inside the IIFE (subsequent lines).
Ronin = (function() { __extends(Ronin, Ninja); function Ronin(numOfShurikens) { Ronin.__super__.constructor.call( // (*) this, numOfShurikens + 1); } return Ronin; })();Again, the IIFE is not needed. The __extends() function that was defined previously is used to perform all of the necessary wiring from subclass to superclass. Caveat: the first argument refers to function Ronin() below, because that function is hoisted [3] (roughly: moved to the top of the surrounding function). It would have been better to invoke __extends() after the declaration of Ronin(), to make that fact explicit. Furthermore at (*), Ronin refers to a binding inside the IIFE environment which keeps that environment alive after leaving the IIFE.
The invocation of the super-constructor is a typical JavaScript technique: You invoke the super-constructor as a function (no new!) and give it the current this, so that it adds its properties, but does not create a new instance.