CoffeeScript: why won't my class work without prefixing with window? - coffeescript

I have an external file where I have defined my class:
class MyClass
constructor: ->
alert 'hello'
When the CoffeeScript is compiled into JavaScript, it wraps it with a closure. So when I try to use it in some JavaScript:
$(function(){
var ob = new MyClass();
});
I get the error:
Uncaught ReferenceError: MyClass is not defined
but if I prefix the class name with window, it will work:
class window.MyClass
constructor: ->
alert 'hello'
How can I define my class without prefixing with window?

You can compile the CoffeeScript with the --bare but that's generally not recommended because you may pollute the global namespace.
My suggestion is to attach the class to the window object, like your second example, or even better, use this namespace function from the CoffeeScript docs to attach your classes to a single global object attached to the window
namespace = (target, name, block) ->
[target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3
top = target
target = target[item] or= {} for item in name.split '.'
block target, top
# Usage:
#
namespace 'Hello.World', (exports) ->
# `exports` is where you attach namespace members
exports.hi = -> console.log 'Hi World!'
namespace 'Say.Hello', (exports, top) ->
# `top` is a reference to the main namespace
exports.fn = -> top.Hello.World.hi()
Say.Hello.fn() # prints 'Hi World!'
Source: CoffeeScript FAQ

Related

Coffeescript classes and scope

While learning coffeescript, I'm trying to create an instance of class A inside a method of class B, this is the code:
class #A
constructor: (#x) ->
show: ->
alert #x
class #B
constructor: (#y) ->
show: ->
a = new #A("eric")
alert a.x
alert #y
b = new #B("daniel")
b.show()
the error is TypeError: undefined is not a function.
Any help is appreciated.
Thanks
You have two problems:
# is just another way of saying this in CoffeeScript. That's all it means.
Classes are (more or less) just variables or properties like any other in CoffeeScript.
So when you say #A, you're just looking for the A property of this and your show is really saying:
a = new this.A("eric")
In that context, # will be an instance of B and Bs don't have A properties. Instead you should just say:
a = new A('eric')
Using # when defining a class:
class #A
#...
is just a way to make a class globally available. At the top level, # will (almost always) be window in a browser so you're really saying:
class window.A
#...
and window properties are globals. Keep in mind that each CoffeeScript file is wrapped in a function when it is converted to JavaScript:
Although suppressed within this documentation for clarity, all CoffeeScript output is wrapped in an anonymous function: (function(){ ... })(); This safety wrapper, combined with the automatic generation of the var keyword, make it exceedingly difficult to pollute the global namespace by accident.
So if you just said:
class A
then A would only be available to other code in that file. Saying:
class #A
makes A global.
If you're only working with one file then you don't need the #s on your classes:
class A
constructor: (#x) ->
show: ->
alert #x
class B
constructor: (#y) ->
show: ->
a = new A("eric")
alert a.x
alert #y
b = new B("daniel")
b.show()
Don't get in the habit of prefixing everything with #, only use it on classes when you need it and you know exactly what it will do. Even when you need it, there are better ways: use require.js to manage your dependencies, use an global application-specific object to manage scopes, ...

Create coffeescript class

I have simple class in coffeescript (this class is located in file.js.coffee):
class ExampleClass
constructor: (arguments) ->
makeSTH: (page) ->
makeSTHElse: (data) =>
I have another coffee file. I included above file and I tried to create instance of ExampleClass this way:
/#= require file.js.coffee
class ExampleClass2
constructor: (arguments) ->
#ex = new ExampleClass(sth)
But I got something like this:
ReferenceError: ExampleClass is not defined
I don't know how to correctly reference to ExampleClass. Thanks for all answers and sorry for my English.
CoffeeScript will compile each of the source file as a separated compilation unit. Each of the compilation unit will be wrapped inside a block, so that the global namespace won't be polluted by mistake. So, ExampleClass actually get compiled to something like:
(function () {
var ExampleClass;
ExampleClass = function (args) {}
...
}).call(this);
You can see that ExampleClass can only be accessed from the same source. In order to access it from other source file, you need to bind it to window.
class window.ExampleClass
constructor: (args) ->
...
PS. you're not allowed to use arguments as formal parameter name in CoffeeScript, as it has special meaning in JavaScript.
And /#= require file.js.coffee is not valid in CoffeeScript, you need to remove the leading /. I think that's just a typo.

When does the "fat arrow" (=>) bind to "this" instance

The fat arrow can be used in different settings but it somehow doesn't
always bind to the instance I want.
The fat arrow binds at 3 occasions
when declaring a method
when declaring a function within a method
when declaring a function in global context
1. When declaring a method
When the Coffeescript compiler encouters the following syntactical pattern
within a class declaration
class A
somemethod: (paramlist) =>
This will yield the following code within the constructor of class A
this.somemethod = __bind(this.somemethod, this);
That is the definition for that instance is overwriting the initial assignment
with a bound version of the function
2. When declaring a function within a method
When you define a function with a fat arrow within a method the Coffeescript compiler
automatically creates a closure and and shadows this of the outer method into the variable
_this. Any reference to # within the inner function will use the variable _this
in the generated javascript code
somemethod: ->
=> #someCall()
And this is the corresponding Javascript
A.prototype.somemethod = function() {
//_this references this
var _this = this;
return function() {
//and _this is now used within the inner function
return _this.someCall();
};
};
A definition of a function without the fat arrow doesn't create that closure for you.
3. When declaring a function in global context
If you define a free floating function (meaning as a method in a class and not within another function/method) just like this
foo = => #bar
Then the corresponding Javascript will look like this
var foo,
_this = this;
foo = function() {
return _this.bar;
};
The interesting thing here again is that this is being assigned to _this which enables the definition of foo to close over _this.
The important part however is that this is always the global context of your execution environment. If you are in the browser it will be the window object. If you are running node.js it will be the module you are just running.
Warning: You shouldn't define any function accessing your global context anyway. This calls for trouble.

Getting Coffeescript to create a local variable in a FOR loop

How can I get dealViewItem into the scope of the FOR loop? Currently, dealViewItem is scoped outside of it, and all my event listeners are added to the last dealViewItem.
for deal in dealArray
dealViewItem = dealViewFactory.DealDetail(deal)
dealViewItem.addEventListener 'click', ->
dealCart.push(deal.dealId)
dealViewItem.setAddedToCart()
btnTakeDeals.setEnabled = true
dealHolder.add(dealViewItem)
this is what the do keyword is for. It will run a function immediately and any local variables with the same name as one of the arguments will be passed into it, ensuring proper closure scope.
for deal in dealArray
do (deal) ->
dealViewItem = dealViewFactory.DealDetail(deal)
dealViewItem.addEventListener 'click', ->
dealCart.push(deal.dealId)
dealViewItem.setAddedToCart()
btnTakeDeals.setEnabled = true
dealHolder.add(dealViewItem)
Check out the compiled version here
do can also be used outside of loops for self executing functions.
#coffeescript
do ->
foo = 'bar'
// javascript
(function() {
var foo;
return foo = bar;
})();

Namespacing in coffeescript

Is there any intrinsic support for namespacing in coffeescript?
Adequate namespacing seems like something coffeescript could really help with although I don't seem to be able to find anything to suggest that there is support for this.
I prefer using this pattern for "namespacing". It isn't really a namespace but an object tree, but it does the job:
Somewhere in the startup of the app, you define the namespaces globally (replace window with exports or global based on your environment.
window.App =
Models: {}
Collections: {}
Views: {}
Then, when you want to declare classes, you can do so:
class App.Models.MyModel
# The class is namespaced in App.Models
And when you want to reference it:
myModel = new App.Models.MyModel()
If you don't like the global way of defining namespaces, you can do so before your class:
window.App.Models ?= {} # Create the "namespace" if Models does not already exist.
class App.Models.MyModel
A way to make it simple to reference the class both in it's own "namespace" (the closed function) and the global namespace is to assign it immediately. Example:
# Define namespace unless it already exists
window.Test or= {}
# Create a class in the namespace and locally
window.Test.MyClass = class MyClass
constructor: (#a) ->
# Alerts 3
alert new Test.MyClass(1).a + new MyClass(2).a
As you see, now you can refer to it as MyClass within the file, but if you need it outside it's available as Test.MyClass. If you only want it in the Test namespace you can simplify it even more:
window.Test or= {}
# Create only in the namespace
class window.Test.MyClass
constructor: (#a) ->
Here's my personal implementation :
https://github.com/MaksJS/Namespace-in-CoffeeScript
How to use in the browser :
namespace Foo:SubPackage1:SubPackage2:
class Bar extends Baz
#[...]
How to use in CommonJS environment :
require './path/to/this/file' # once
namespace Foo:SubPackage1:SubPackage2:
class Bar extends Baz
#[...]
From the section about namespacing on the wiki: https://github.com/jashkenas/coffee-script/wiki/FAQ
# Code:
#
namespace = (target, name, block) ->
[target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3
top = target
target = target[item] or= {} for item in name.split '.'
block target, top
# Usage:
#
namespace 'Hello.World', (exports) ->
# `exports` is where you attach namespace members
exports.hi = -> console.log 'Hi World!'
namespace 'Say.Hello', (exports, top) ->
# `top` is a reference to the main namespace
exports.fn = -> top.Hello.World.hi()
Say.Hello.fn() # prints 'Hi World!'
You must really check out CoffeeToaster:
https://github.com/serpentem/coffee-toaster
It comes with a packaging system that when enabled will use your folder's hierarchy as namespaces declarations to your classes if you want so, then you can extends classes from multiple files, do imports and son, such as like:
#<< another/package/myclass
class SomeClass extends another.package.MyClass
The build configuration is extremely minimalist and simple, made to be obvious:
# => SRC FOLDER
toast 'src_folder'
# => VENDORS (optional)
# vendors: ['vendors/x.js', 'vendors/y.js', ... ]
# => OPTIONS (optional, default values listed)
# bare: false
# packaging: true
# expose: ''
# minify: false
# => HTTPFOLDER (optional), RELEASE / DEBUG (required)
httpfolder: 'js'
release: 'www/js/app.js'
debug: 'www/js/app-debug.js'
There's also a debug option that compile files individually for ease the debugging processes and another useful features.
Hope it helps.
Note that it is possible to write:
class MyObject.MyClass
constructor: () ->
initializeStuff()
myfunction: () ->
doStuff()
if you declared an object/ns MyObject.
And while we're at it, here's my implementation of a jquery-ns-function:
(function($) {
$.namespace = function(namespace, initVal) {
var nsParts = namespace.split("."),
nsPart = nsParts.shift(),
parent = window[nsPart] = window[nsPart] || {},
myGlobal = parent;
while(nsPart = nsParts.shift()) {
parent = parent[nsPart] = parent[nsPart] || {};
}
return myGlobal;
}
})(jQuery);
I Strongly suggest using requirejs.org or similar battle tested module loaders.
Especially if you want to load stuff asynchronously.
Rolling your own namespacing/module scheme is really hard if you disregard
the simply, easy and naive approaches
As I'm also busy to learn the best way of structuring the files and use coffeescript in combination with backbone and cake, I have created a small project on github to keep it as a reference for myself, maybe it will help you too around cake and some basic things. All .js (with cake compiled files) are in www folder so that you can open them in your browser and all source files (except for cake configuration) are in src folder. In this example, all .coffee files are compiled and combined in one output .js file which is then included in html.
Based on some of the answers here on StackOverflow I have created small util.coffee file (in src folder) that exposes "namespaces" to the rest of the code.