Introducing CoffeeScript
- Tutorial
The article is not an exhaustive description of the CoffeeScript programming language, namely an acquaintance, an overview of some interesting features. The target audience is those who have not yet looked towards CoffeeScript, but one way or another use JavaScript in their projects.
CoffeeScript is a small language that translates into JavaScript. Its documentation fits on one page - coffeescript.org and is compact and clear. I even doubted the necessity of this article when there is such a cool description “from the manufacturer”, but still I ventured to put emphasis and clarify some details.
If you dig a bit of history, then since 2009, the language has been written in Ruby, since 2010 - it is written in CoffeeScript itself.
And in Ruby on Rails, starting with version 3.1, it "replaced" JavaScript.
Essentially, CoffeeScript is just syntactic sugar over JavaScript. So, its value is that it allows you to more clearly express your thoughts and understand others.
JavaScript (read ECMAScript), of course, also does not stand still, it is developing. Including adopting some ideas from CoffeeScript. But speaking of cross-browser JavaScript, I personally have great suspicions that a bright future with advanced JavaScript will come soon. And CoffeeScript now allows you to enjoy the fruits of technological progress.
In this vein, one cannot but mention TypeScript, in a certain sense, a competitor to CoffeeScript. It is positioned as a superset of JavaScript, adding new features to the language, largely reflecting the future of JavaScript. From this position he is more interesting.
But CoffeeScript has the advantage that it does not need to maintain compatibility with JavaScript, which, in my opinion, gives more freedom and allows you to make the language more expressive. So, there is at least one noteworthy CoffeeScript alternative. But back to the topic.
Ok, how to use this with your CoffeeScript?
In my opinion, it is most convenient to work with it as with the node.js. module. It is put as simple as
possible : We create two folders, for definiteness we will name them
Create a file
After that, run the translator:
As a result, the file
Of course, it’s not interesting to run a translator for everyone. Running the command
forces you to monitor all changes to the files in the folder
Let's move on to the language itself. Let's write a simple CoffeeScript code:
Its JavaScript equivalent:
Here we create two functions that calculate the square and cube of a number, respectively.
First of all, note that all the code is hidden inside an anonymous function that we immediately call.
This technique allows you to hide all local variables inside the function, without worrying that they will clog the global scope. Below in the article we will omit this function for clarity.
Next, note that the declaration of all local variables is
The arrow
And also note that there is no need to add a word
CoffeeScript adds default values for function parameters, which is not available in JavaScript.
CoffeeScript example:
JavaScript equivalent:
The JavaScript implementation boils down to checking the parameter
Another detail that the example illustrates is that indentation of blocks is not using curly braces, but indents, as in Python.
Another thing that annoys in JavaScript is a very verbose iteration over the properties of objects.
The fact is that in most cases when walking around an object, its own properties are of interest, not the properties of the prototype.
And to do it every time
The solution in the style of jQuery.each () was not forbidden by anyone, but it is inferior in effectiveness to the old-fashioned one
We look how to make it cool:
Equivalent:
In JavaScript, the == operator behaves mildly strange. It is much safer to use ===. Therefore, CoffeeScript converts the == operator to ===, protecting novice developers from traps in JavaScript. Although one case comes to mind when the == operator is still useful. This comparison with
And at the output:
We pass to classes. Just in case, we clarify that we will call classes constructor functions of objects.
Consider an example:
Even intuitively, you can guess what is happening. The base class
Let's pay attention to the class
In methods
I will not languish and, finally, we will move on to the js-version of our classes.
Inheritance is based on a variation of the classical function
The implementation is quite simple. Of course, when compared with other JavaScript libraries that provide a convenient cross-browser implementation of classes in pure JavaScript.
The downside of fancy libraries is that it's not always easy to figure out how they work inside.
And the function is
Another important criterion is the efficiency of the generated code. So, with this everything is in order, I did not find any nonsense. Functions as expected are not added as class properties, but to the prototype. I was also pleased that the default value of class properties is also added to the prototype.
Consider a very simple class:
The output is JavaScript:
Here, the so-called asymmetry of the object's properties for reading and writing is used.
In real life, the default property value is almost always more profitable to add to the object prototype.
Until we need to change this default value, we do not waste extra memory for each object of a certain class. But let's say we decided to change the value of this property like this:
This creates a personal property of
The only thing that can be confusing in this approach is that when accessing the property that is in the prototype, you have to move along the prototype chain. And this is not given for free. But on modern engines this is not essential, especially against the background of radical optimization of memory usage, but old IEs, in which degradation was felt, are gradually disappearing into oblivion.
Another cool feature is the appointment of event handlers for object methods. Example:
Issue:
In order to specify the method of the same object in pure JavaScript as an event handler, you have to get out.
One of the most common ways is to create a closure. In CoffeeScript, this crutch is not needed. It is enough to indicate the function of the handler not as
If you need to connect pure JavaScript code, then this is also easy to do:
The output is:
And of course, there are many chips for working with arrays and objects. To illustrate, consider one.
For example, suppose we want to get an array of cubes of numbers from 1 to 5.
In CoffeeScript, just write:
In verbose JavaScript, we get:
I hope for dating should be enough. Further welcome to coffeescript.org .
Well, as expected, a few conclusions:
The main thing is to understand what CoffeeScript generates. Then it turns from an extra suspicious layer of abstraction into a powerful tool.
CoffeeScript is a small language that translates into JavaScript. Its documentation fits on one page - coffeescript.org and is compact and clear. I even doubted the necessity of this article when there is such a cool description “from the manufacturer”, but still I ventured to put emphasis and clarify some details.
Introduction
If you dig a bit of history, then since 2009, the language has been written in Ruby, since 2010 - it is written in CoffeeScript itself.
And in Ruby on Rails, starting with version 3.1, it "replaced" JavaScript.
Essentially, CoffeeScript is just syntactic sugar over JavaScript. So, its value is that it allows you to more clearly express your thoughts and understand others.
JavaScript (read ECMAScript), of course, also does not stand still, it is developing. Including adopting some ideas from CoffeeScript. But speaking of cross-browser JavaScript, I personally have great suspicions that a bright future with advanced JavaScript will come soon. And CoffeeScript now allows you to enjoy the fruits of technological progress.
In this vein, one cannot but mention TypeScript, in a certain sense, a competitor to CoffeeScript. It is positioned as a superset of JavaScript, adding new features to the language, largely reflecting the future of JavaScript. From this position he is more interesting.
But CoffeeScript has the advantage that it does not need to maintain compatibility with JavaScript, which, in my opinion, gives more freedom and allows you to make the language more expressive. So, there is at least one noteworthy CoffeeScript alternative. But back to the topic.
Code translation
Ok, how to use this with your CoffeeScript?
In my opinion, it is most convenient to work with it as with the node.js. module. It is put as simple as
npm install -g coffee-script
possible : We create two folders, for definiteness we will name them
lib
and src
. Create a file
src/helloWorld.coffee
and write something on CoffeeScript. For instance:console.log('Hello world')
After that, run the translator:
coffee --compile --output lib/ src/
As a result, the file
lib
will be in the folder helloWorld.js
, ready for execution. Of course, it’s not interesting to run a translator for everyone. Running the command
coffee -o lib/ -cw src/
forces you to monitor all changes to the files in the folder
src
and independently translate them into JavaScript code.Syntax
Functions
Let's move on to the language itself. Let's write a simple CoffeeScript code:
square = (x) -> x * x
cube = (x) -> square(x) * x
Its JavaScript equivalent:
(function() {
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
}).call(this);
Here we create two functions that calculate the square and cube of a number, respectively.
First of all, note that all the code is hidden inside an anonymous function that we immediately call.
This technique allows you to hide all local variables inside the function, without worrying that they will clog the global scope. Below in the article we will omit this function for clarity.
Next, note that the declaration of all local variables is
var cube, square
placed at the beginning. Which protects against a common mistake when a variable for no reason, for no reason has become global because of the banal forget to add an ad var
. The arrow
->
replaces the word function
. And also note that there is no need to add a word
return
. It is added automatically to the last expression in the function.Default Parameter Values
CoffeeScript adds default values for function parameters, which is not available in JavaScript.
CoffeeScript example:
fill = (container, liquid = "coffee") ->
"Filling the #{container} with #{liquid}..."
JavaScript equivalent:
var fill;
fill = function(container, liquid) {
if (liquid == null) {
liquid = "coffee";
}
return "Filling the " + container + " with " + liquid + "...";
};
The JavaScript implementation boils down to checking the parameter
liquid
for equality null
or undefined
. Another detail that the example illustrates is that indentation of blocks is not using curly braces, but indents, as in Python.
Iterate over object properties
Another thing that annoys in JavaScript is a very verbose iteration over the properties of objects.
The fact is that in most cases when walking around an object, its own properties are of interest, not the properties of the prototype.
And to do it every time
for
and in it immediately the check is a hasOwnProperty
little tiring. The solution in the style of jQuery.each () was not forbidden by anyone, but it is inferior in effectiveness to the old-fashioned one
for
. We look how to make it cool:
yearsOld = max: 10, ida: 9, tim: 11
for own child, age of yearsOld
console.log "#{child} is #{age}"
Equivalent:
var age, child,
__hasProp = {}.hasOwnProperty;
for (child in yearsOld) {
if (!__hasProp.call(yearsOld, child)) continue;
age = yearsOld[child];
console.log("" + child + " is " + age);
}
Pleasant trifles
In JavaScript, the == operator behaves mildly strange. It is much safer to use ===. Therefore, CoffeeScript converts the == operator to ===, protecting novice developers from traps in JavaScript. Although one case comes to mind when the == operator is still useful. This comparison with
null
that allows you to check null
and undefined
in one fell swoop. In CoffeeScript, the operator is for this purpose ?
. Consider an example:alert "I knew it!" if elvis?
And at the output:
if (typeof elvis !== "undefined" && elvis !== null) {
alert("I knew it!");
}
Classes
We pass to classes. Just in case, we clarify that we will call classes constructor functions of objects.
Consider an example:
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
class Snake extends Animal
move: ->
alert "Slithering..."
super 5
class Horse extends Animal
move: ->
alert "Galloping..."
super 45
sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"
sam.move()
tom.move()
Even intuitively, you can guess what is happening. The base class
Animal
and its two heirs are described : Snake
and Horse
. Let's pay attention to the class
Animal
. The entry @name
in the constructor parameters is a convenient abbreviation that defines the property of the name class and automatically assigns the value passed to it in the constructor. In the move method, the entry @name
is short for this.name
. In methods
move
in subclasses, it super
calls the parent method of the same name. After all, the truth is, when we are in a child class, a reference to the parent is sometimes needed only to refer to the parent class method of the same name. Other cases do not even occur. I will not languish and, finally, we will move on to the js-version of our classes.
var Animal, Horse, Snake, sam, tom, _ref, _ref1,
__hasProp = {}.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; };
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
Snake = (function(_super) {
__extends(Snake, _super);
function Snake() {
_ref = Snake.__super__.constructor.apply(this, arguments);
return _ref;
}
Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__super__.move.call(this, 5);
};
return Snake;
})(Animal);
Horse = (function(_super) {
__extends(Horse, _super);
function Horse() {
_ref1 = Horse.__super__.constructor.apply(this, arguments);
return _ref1;
}
Horse.prototype.move = function() {
alert("Galloping...");
return Horse.__super__.move.call(this, 45);
};
return Horse;
})(Animal);
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
Inheritance is based on a variation of the classical function
extend
. The implementation is quite simple. Of course, when compared with other JavaScript libraries that provide a convenient cross-browser implementation of classes in pure JavaScript.
The downside of fancy libraries is that it's not always easy to figure out how they work inside.
And the function is
extend
very well described in many sources, for example, here javascript.ru/tutorial/object/inheritance#nasledovanie-na-klassah-funkciya-extend .Efficiency
Another important criterion is the efficiency of the generated code. So, with this everything is in order, I did not find any nonsense. Functions as expected are not added as class properties, but to the prototype. I was also pleased that the default value of class properties is also added to the prototype.
Consider a very simple class:
class Foo
bar: 10
The output is JavaScript:
var Foo;
Foo = (function() {
function Foo() {}
Foo.prototype.bar = 10;
return Foo;
})();
Here, the so-called asymmetry of the object's properties for reading and writing is used.
In real life, the default property value is almost always more profitable to add to the object prototype.
Until we need to change this default value, we do not waste extra memory for each object of a certain class. But let's say we decided to change the value of this property like this:
obj = new Foo()
obj.bar = 500
This creates a personal property of
bar
the object obj
. In this case, the property of the bar
prototype object obj
is still equal to 10. Everything is safe and effective. The only thing that can be confusing in this approach is that when accessing the property that is in the prototype, you have to move along the prototype chain. And this is not given for free. But on modern engines this is not essential, especially against the background of radical optimization of memory usage, but old IEs, in which degradation was felt, are gradually disappearing into oblivion.
Assigning Event Handlers
Another cool feature is the appointment of event handlers for object methods. Example:
Account = (customer, cart) ->
@customer = customer
@cart = cart
$('.shopping_cart').bind 'click', (event) =>
@customer.purchase @cart
Issue:
var Account;
Account = function(customer, cart) {
var _this = this;
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').bind('click', function(event) {
return _this.customer.purchase(_this.cart);
});
};
In order to specify the method of the same object in pure JavaScript as an event handler, you have to get out.
One of the most common ways is to create a closure. In CoffeeScript, this crutch is not needed. It is enough to indicate the function of the handler not as
->
, but =>
. After that, the this
handler will refer to the base object.Pure JavaScript Integration
If you need to connect pure JavaScript code, then this is also easy to do:
hi = `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`
The output is:
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
Arrays
And of course, there are many chips for working with arrays and objects. To illustrate, consider one.
For example, suppose we want to get an array of cubes of numbers from 1 to 5.
In CoffeeScript, just write:
cubes = (Math.pow(num, 3) for num in [1..5])
In verbose JavaScript, we get:
var cubes, num;
cubes = (function() {
var _i, _results;
_results = [];
for (num = _i = 1; _i <= 5; num = ++_i) {
_results.push(Math.pow(num, 3));
}
return _results;
})();
Conclusion
I hope for dating should be enough. Further welcome to coffeescript.org .
Well, as expected, a few conclusions:
- CoffeeScript increases the expressiveness of the code, simplifies and speeds up both the initial development and further support of the code.
- The training is very fast (it took me a couple of days to get involved).
- Convenient support from WebStorm (For other IDEs there are also plugins, but I can not say anything about their quality)
- Big community
- Protects especially novice developers from many mistakes.
The main thing is to understand what CoffeeScript generates. Then it turns from an extra suspicious layer of abstraction into a powerful tool.