CoffeeScript

CoffeeScript (; кофи скрипт) — язык программирования, транслируемый в JavaScript. CoffeeScript добавляет синтаксический сахар в духе Ruby, Python, Haskell и Erlang для того, чтобы улучшить читаемость кода и уменьшить его размер.
CoffeeScript позволяет писать более компактный код по сравнению с JavaScript.
JavaScript-код, получаемый трансляцией из CoffeeScript, полностью проходит проверку JavaScript Lint.

История 
Создателем языка является Джереми Ашкенас.

Изначально компилятор был написан на Ruby, но в версии 0.5, которая вышла 21 февраля 2010 года, компилятор был реализован на самом же CoffeeScript.

CoffeeScript был радушно воспринят в Ruby-сообществе. Встроенная поддержка CoffeeScript была добавлена в веб-фреймворк Ruby on Rails с версии 3.1.

Преимущества использования 
Использование пробелов как разграничительных знаков(вместо скобок, точек с запятой и прочих), делает CoffeeScript кратким. По сравнению с JavaScript, строка для того же конкретного кода в CoffeeScript сокращается примерно до половины (примерно на 55% меньше). Так же CoffeeScript позволяет избежать проблем с объявлением области действия в программе, поскольку в отличие от JavaScript использование ключевого слова var перед объявлением переменной не требуется. Помимо этого, в CoffeeScript есть ряд удобных функций, таких как осмысление массивов, псевдонимы прототипов и классы, которые еще больше сокращают количество вводимых символов.

Недостатки использования 
Дополнительный этап компиляции между написанным кодом и кодом на JavaScript увеличивает общее время компиляции программы. CoffeeScript не является широко используемым, из за чего сложней искать источники информации по нему и кооперировать с другими разработчиками

Синтаксис

Особенности 
- отсутствие точек с запятой

- фигурные скобки ({}) заменены табуляцией

Комментарии 
Синтаксис для комментариев заимствован из Ruby, где каждый однострочный комментарий начинается со знака решетки "#", а многострочные комментарии заключены между тремя символами решетки:# A single line comment

###
    A multiline 
    comment
###

Пробелы 
Вдохновившись Python, в CoffeeScript вместо фигурных скобок используется табуляция

Переменные 
Неподдерживаемость CoffeeScript глобальных переменных предотвращает ошибки доступа, которые могли возникнуть в JavaScript при случайном объявлении глобальной переменной.

CoffeeScript:myVariable = "test"JavaScript:var myVariable;
myVariable = "test";

Функции 
CoffeeScript удаляет довольно многословный оператор функции и заменяет его тонкой стрелкой: ->. Функции могут быть однострочными или отступать на несколько строк. Последнее выражение в функции неявно возвращается.
CoffeeScript:func = -> "bar"CoffeeScript:func = ->
  # An extra line
  "bar"JavaScript:var func;
func = function() {
  return "bar";
};

Аргументы функций 
Аргументы функции записываются в круглые скобки перед стрелкой. Есть поддержка аргументов по умолчанию.

CoffeeScript:times = (a = 1, b) -> a * bJavaScript:var times;
times = function(a, b) {
    if(a == null){
        a = 1;
    }
  return a * b;
};Так же можно использовать слайсы для приема нескольких аргументов

CoffeeScript:a = "Howdy!"

alert a
# Equivalent to:
alert(a)

alert inspect a
# Equivalent to:
alert(inspect(a))JavaScript:var a;
a = "Howdy!";
alert(a);
alert(a);
alert(inspect(a));
alert(inspect(a));

Вызов функций 
Функции можно вызывать точно так же, как и в JavaScript, с помощью скобок (), apply() или call(). Однако, как и в Ruby, CoffeeScript автоматически вызывает функции, если они вызываются хотя бы с одним аргументом.

CoffeeScript:myVariable = "test"JavaScript:var myVariable;
myVariable = "test";

Объектные литералы и объявление массивов 
Объектные литералы задаются точно так же, как и в JavaScript, с помощью пары скобок и операторов ключ/значение. Однако, как и в случае с вызовом функций, в CoffeeScript скобки необязательны. Вместо запятых можно использовать отступы и новые строки.

CoffeeScript:object1 = {one: 1, two: 2}

# Without braces
object2 = one: 1, two: 2

# Using new lines instead of commas
object3 = 
  one: 1
  two: 2

User.create(name: "John Smith")JavaScript:var object1, object2, object3;
object1 = {
  one: 1,
  two: 2
};
object2 = {
  one: 1,
  two: 2
};
object3 = {
  one: 1,
  two: 2
};
User.create({
  name: "John Smith"
});Аналогично, в массивах вместо запятых могут использоваться пробельные символы, хотя квадратные скобки ([]) по-прежнему обязательны.

CoffeeScript:array1 = [1, 2, 3]

array2 = [
  1
  2
  3
]

array3 = [1,2,3,]JavaScript:var array1, array2, array3;
array1 = [1, 2, 3];
array2 = [1, 2, 3];
array3 = [1, 2, 3];

Условные операторы 
Если оператор if расположен в одной строке, необходимо использовать ключевое слово then, чтобы CoffeeScript знал, когда начинается блок. Условные операторы (?:) не поддерживаются, вместо них следует использовать однострочный оператор if/else.

CoffeeScript:if true == true
  "We're ok"

if true != true then "Panic"

# Equivalent to:
#  (1 > 0) ? "Ok" : "Y2K!"
if 1 > 0 then "Ok" else "Y2K!"JavaScript:if (true === true) {
  "We're ok";
}
if (true !== true) {
  "Panic";
}
if (1 > 0) {
  "Ok";
} else {
  "Y2K!";
}В CoffeeScript также используется идиома Ruby, позволяющая использовать суффиксные операторы if.

CoffeeScript:alert "It's cold!" if heat < 5JavaScript:if (heat < 5) {
  alert("It's cold!");
}Вместо восклицательного знака (!) для отрицания можно также использовать ключевое слово not или оператор unless

CoffeeScript:if not true then "Panic"unless true
  "Panic"JavaScript:if (!true) {
  "Panic";
}Аналогично not, в CoffeeScript также вводится оператор is, который работает как === в JavaScript.

CoffeeScript:if true is 1
  "Type coercion fail!"JavaScript:if (true === 1) {
  "Type coercion fail!";
}

Интерполяция строк 
CoffeeScript привносит в JavaScript интерполяцию строк в стиле Ruby. Строки в двойных кавычках могут содержать теги #{}, которые содержат выражения, подлежащие интерполяции в строку.

CoffeeScript:favourite_color = "Blue. No, yel..."
question = "Bridgekeeper: What... is your favourite color?
            Galahad: #{favourite_color}
            Bridgekeeper: Wrong!
"JavaScript:var favourite_color, question;
favourite_color = "Blue. No, yel...";
question = "Bridgekeeper: What... is your favourite color?            Galahad: " + favourite_color + "            Bridgekeeper: Wrong!            ";

Циклы и вычисления 
...

CoffeeScript:

JavaScript:

Массивы 
CoffeeScript черпает вдохновение из Ruby, когда речь идет о создании массивов с помощью диапазонов. Диапазоны создаются двумя числовыми значениями, первой и последней позициями в диапазоне, разделенными ... или ..... Если диапазон не имеет никакого префикса, CoffeeScript расширяет его до массива.

CoffeeScript:
range = [1..5]

JavaScript:
var range;
range = [1, 2, 3, 4, 5];Если же диапазон указывается сразу после переменной, то CoffeeScript преобразует его в вызов метода slice().

CoffeeScript:
firstTwo = ["one", "two", "three"][0..1]numbers = [0..9]
numbers[3..5] = [-3, -4, -5]my = "my string"[0..2]

JavaScript:
var firstTwo;
firstTwo = ["one", "two", "three"].slice(0, 2);var numbers, _ref;
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
[].splice.apply(numbers, [3, 3].concat(_ref = [-3, -4, -5])), _ref;var my;
my = "my string".slice(0, 3);Проверка наличия значения внутри массива всегда является проблемой в JavaScript, тем более что функция indexOf() пока не имеет полной кроссбраузерной поддержки. В CoffeeScript эта проблема решается с помощью оператора in:

CoffeeScript:
words = ["rattled", "roudy", "rebbles", "ranks"]
alert "Stop wagging me" if "ranks" in words 

JavaScript:
var words;
var __indexOf = Array.prototype.indexOf || function(item) {
  for (var i = 0, l = this.length; i < l; i++) {
    if (this[i] === item) return i;
  }
  return -1;
};
words = ["rattled", "roudy", "rebbles", "ranks"];
if (__indexOf.call(words, "ranks") >= 0) {
  alert("Stop wagging me");
}

Классы 

CoffeeScript использует собственный прототип JavaScript для создания классов, добавляя немного синтаксического сахара для наследования статических свойств и сохранения контекста. В CoffeeScript используются функции-конструкторы, что означает возможность инстанцирования классов с помощью оператора new. CoffeeScript предоставляет сокращение для общего шаблона установки свойств экземпляра класса. Префикс аргумента @ позволяет CoffeeScript автоматически устанавливать аргументы как свойства экземпляра в конструкторе.
CoffeeScript:class Animal
  constructor: (@name) ->

JavaScript:
var Animal;
Animal = (function() {
  function Animal(name) {
    this.name = name;
  }
  return Animal;
})();

Свойства экземпляра 
Добавление дополнительных свойств экземпляра в класс очень просто, это точно такой же синтаксис, как и добавление свойств к объекту. Свойства должны быть правильно расположены с отступом внутри тела класса.

CoffeeScript:
class Animal
  price: 5

  sell: (customer) ->

animal = new Animal
animal.sell(new Customer)

JavaScript:
var Animal, animal;
Animal = (function() {
  function Animal() {}
  Animal.prototype.price = 5;
  Animal.prototype.sell = function(customer) {};
  return Animal;
})();
animal = new Animal;
animal.sell(new Customer);

Статичные свойства 
Внутри определения класса, ключевое слово this и @ ссылаются на объект класса

CoffeeScript:
class Animal
  this.find_ = (name) -> 
  @.age_ = (age) ->

Animal.find_("Parrot")

JavaScript:
var Animal;
Animal = (function() {
  function Animal() {}
  Animal.find_ = function(name) {};
  Anumal.age_ = functiona(age){};
  return Animal;
})();
Animal.find_("Parrot");

Наследование 
Наследование класса происходит при помощи ключевого слова extends

CoffeeScript:
class Animal
  constructor: (@name) ->

  alive: ->
    false

class Parrot extends Animal
  constructor: ->
    super("Parrot")

  dead: ->
    not @alive()

JavaScript:
var Animal, Parrot;
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;
};
Animal = (function() {
  function Animal(name) {
    this.name = name;
  }
  Animal.prototype.alive = function() {
    return false;
  };
  return Animal;
})();
Parrot = (function() {
  __extends(Parrot, Animal);
  function Parrot() {
    Parrot.__super__.constructor.call(this, "Parrot");
  }
  Parrot.prototype.dead = function() {
    return !this.alive();
  };
  return Parrot;
})();Cтатические свойства копируются в подклассы, а не наследуются по прототипу, как свойства экземпляра. Это связано с особенностями реализации прототипической архитектуры JavaScript и является трудноразрешимой проблемой.

Идиомы

Each 
В JavaScript для итерации по каждому элементу массива можно использовать либо недавно добавленную функцию forEach(), либо же цикл for в стиле C. Хотя синтаксис forEach() гораздо более лаконичен и удобен для чтения, он страдает тем недостатком, что функция обратного вызова будет вызываться на каждой итерации массива, и поэтому работает гораздо медленнее, чем эквивалентный цикл for. Синтаксис CoffeeScript обеспечивает ту же выразительность, что и forEach(), но без ограничений по скорости.
CoffeeScript:myFunction(item) for item in array

JavaScript:
var item, _i, _len;
for (_i = 0, _len = array.length; _i < _len; _i++) {
  item = array[_i];
  myFunction(item);
}

Includes 
Проверка того, находится ли значение внутри массива, обычно выполняется с помощью функции indexOf(). CoffeeScript использует Array.prototype.indexOf() и, при необходимости, шимминг, чтобы определить, находится ли значение внутри массива. К сожалению, это означает, что аналогичный синтаксис не будет работать для строк. Приходится возвращаться к использованию indexOf() и проверять, не будет ли результат отрицательным или, что еще лучше, использовать побитовый оператор, чтобы не делать сравнение -1.
CoffeeScript:string   = "a long test string"
included = !!~ string.indexOf "test"

JavaScript:
var included, string;
string = "a long test string";
included = !!~string.indexOf("test");

Итерации 
Для итерации в JavaScript используется оператор in. В CoffeeScript Вместо этого оператор был переименован в of, и его можно использовать следующим образом:

CoffeeScript:
object = {one: 1, two: 2}
alert("#{key} = #{value}") for key, value of object

JavaScript:
var key, object, value;
object = {
  one: 1,
  two: 2
};
for (key in object) {
  value = object[key];
  alert("" + key + " = " + value);
}

Min/Max 
Math.max и Math.min принимают несколько аргументов, поэтому можно легко использовать ... для передачи им массива, получая максимальное и минимальное значения в массиве.

CoffeeScript:
Math.max [14, 35, -7, 46, 98]... # 98
Math.min [14, 35, -7, 46, 98]... # -7

JavaScript:
Math.max.apply(Math, [14, 35, -7, 46, 98]);
Math.min.apply(Math, [14, 35, -7, 46, 98]);

And/or 
В руководствах по стилю CoffeeScript указано, что or предпочтительнее, чем ||, а and предпочтительнее, чем &&. Это предпочтение более английского стиля также относится к использованию is вместо == и isnt вместо !=. Одним из чрезвычайно приятных дополнений к CoffeeScript является 'or equals' - паттерн, который рубинисты могут узнать как ||=:

CoffeeScript:
string = "migrating coconuts"
string == string # true
string is string # true

JavaScript:
var string;
string = "migrating coconuts";
string === string;
string === string;

Внешние библиотеки 
Использование внешних библиотек - это то же самое, что вызов функций из библиотек CoffeeScript, поскольку в конечном итоге все компилируется в JavaScript. 

CoffeeScript:
# Use local alias
$ = jQuery

$ ->
  # DOMContentLoaded
  $(".el").click ->
    alert("Clicked!")

JavaScript:
var $;
$ = jQuery;
$(function() {
  return $(".el").click(function() {
    return alert("Clicked!");
  });
});

Private variables 
Ключевое слово do в CoffeeScript позволяет выполнять функции немедленно, что является отличным способом инкапсуляции области видимости и защиты переменных.

Реализация 

На официальном сайте языка есть раздел «try coffeescript», позволяющий выполнять программы на нём online. В отличие, к примеру, от Try Ruby, при этом не будет происходить запросов к серверу, код компилируется и исполняется непосредственно в браузере.

Примеры

Переменные 

CoffeeScript:
age  = 2
male = true
name = "Матвей"

JavaScript:
let age = 2,
    male = true,
    name = "Матвей";

Функции 

CoffeeScript:
say = (speech) ->
  alert speech

say "Привет мир!"

JavaScript с использованием ECMAScript 2015:
const say = speech => alert(speech);
say('Привет мир!');

JavaScript:
var say = function(speech) {
  alert(speech);
};
say("Привет мир!");

Классы и объекты 

CoffeeScript:
class Human
  constructor : (@name) ->

class Baby extends Human
  say   : (msg) -> alert "#{@name} говорит '#{msg}'"
  sayHi : -> @say('здравствуй!')

matt = new Baby("Матвей")
matt.sayHi()

JavaScript с использованием ECMAScript 2015:
class Human {
	constructor(name) {
		this.name = name;
	}
}

class Baby extends Human {
	say(msg) {
		alert(`${this.name} говорит '${msg}'`);
	}
	sayHi()	{
		this.say('здравствуй!');
	}
}

const matt = new Baby('Матвей');
matt.sayHi();

Аналог на JavaScript (именно аналог, а не результат компиляции):
function Human(name){
  this.name = name;
}

function Baby(name){
  Human.call(this, name);
}

Baby.prototype = Object.create(Human.prototype);
Baby.prototype.say = function(msg){
  alert(this.name + ' говорит ' + msg);
};
Baby.prototype.sayHi = function(){
  this.say('здравствуй!');
};
Baby.prototype.constructor = Human;

var matt = new Baby("Матвей");
matt.sayHi();
Примечание: в JavaScript при работе с «классами» (конструктор + прототипы + функции для наследования и смешивания) часто используют обёртки (MooTools, AtomJS и другие).
Аналогия на JavaScript с классовой обёрткой AtomJS:
var Human = Class({
  initialize : function(name) {
    this.name = name;
  }
});

var Baby = Class({
  Extends : Human,
  say : function(msg) {
    alert(this.name + ' говорит ' + msg);
  },
  sayHi : function() {
    this.say('здравствуй!');
  }
});

var matt = new Baby("Матвей");
matt.sayHi();

Пример класса CoffeeScript с различными видами свойств.
class Test
  say   = (msg) -> alert msg       # приватный метод
  @echo = (msg) -> console.log msg # статический метод, записан в Test
  setHi : (msg) ->                 # динамический метод, записан в Test.prototype
    @hi = -> msg                   # динамический метод, записан в экземпляр Test

Компилятор 
Интересным является тот факт, что компилятор для CoffeeScript написан на самом CoffeeScript

См. также 
 Dart
 HaXe
 JavaScript
 TypeScript

Примечания

Литература 
 
 
 CoffeeScript Cookbook, сборник рецептов CoffeeScript от сообщества.
 Smooth CoffeeScript , свободная электронная книга о CoffeeScript. Доступна в двух вариантах с исходным кодом примеров.
 
 
 

 
 Alex MacCaw. The Little Book on CoffeeScript

Ссылки 
 Официальный сайт CoffeeScript
 Репозиторий CoffeeScript на GitHub
 Перевод официальной документации
 Русскоязычная группа по CoffeeScript
 Русскоязычный сайт по CoffeeScript
 Видеокурс (ru)

Смежные проекты:
 Компилятор CoffeeScript для Windows
 CoffeeKup, шаблонизатор и движок для генерации HTML-кода на CoffeeScript.
 Prepros, компилирует на лету CoffeeScript. Под Windows и OSX (также компилирует LESS, Stylus, Haml, Jade, Markdown, Slim, SASS)

Семейство языков программирования JavaScript
Прототипно-ориентированные языки программирования
Транспиляция