CoffeeScript
CoffeeScript ([’kɔ:fɪ skrɪpt]; кофи скрипт) — язык программирования, транслируемый в JavaScript. CoffeeScript добавляет синтаксический сахар в духе Ruby, Python, Haskell и Erlang для того, чтобы улучшить читаемость кода и уменьшить его размер. CoffeeScript позволяет писать более компактный код по сравнению с JavaScript[3]. 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 * b
JavaScript: 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 < 5
JavaScript: 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_ = function(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/MaxMath.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[4]. В отличие, к примеру, от Try Ruby[5], при этом не будет происходить запросов к серверу, код компилируется и исполняется непосредственно в браузере. ПримерыПеременные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 См. такжеПримечания
Литература
Ссылки
Смежные проекты:
|