CoffeeScript
CoffeeScript за 13 минут: весь язык на одной странице через комментированный код — синтаксис, функции, классы, comprehensions и компиляция в JS.
CoffeeScript — это «синтаксический сахар» поверх JavaScript: тот же язык, но с чистым синтаксисом без лишних скобок и точек с запятой. Любой код компилируется в обычный читаемый JS. Весь язык — ниже, в комментариях к коду.
Философия и вывод
Значимые отступы вместо фигурных скобок, минимум знаков препинания.
# Это однострочный комментарий
###
А это блочный комментарий:
всё между тройными решётками — комментарий
###
# Вывод в консоль (это обычный JS console.log)
console.log "Привет, CoffeeScript!" # скобки вызова можно опускать
console.log("Со скобками тоже работает")
# Блоки кода задаются ОТСТУПАМИ, а не { }
# Нет точек с запятой в конце строк
# Нет var — об этом ниже
Переменные
Никакого var: CoffeeScript сам объявляет переменные в безопасной области видимости.
# Просто присваивание — без var/let/const
name = "Аня"
age = 25
pi = 3.14
# CoffeeScript оборачивает весь файл в функцию,
# поэтому переменные не утекают в глобальную область.
# Объявление создаётся автоматически в той области,
# где переменная впервые присвоена.
counter = 0
counter += 1 # 1
# Перезапись существующей переменной не создаёт новую
name = "Борис" # та же переменная name
Строки
Интерполяция через #{ }, многострочные и блочные строки.
user = "Маша"
# Интерполяция работает в двойных кавычках
greeting = "Привет, #{user}!" # "Привет, Маша!"
math = "2 + 2 = #{2 + 2}" # "2 + 2 = 4"
# Одинарные кавычки — без интерполяции (буквально)
raw = 'Тут #{user} не подставится'
# Многострочная строка
letter = "Дорогой друг,
я пишу тебе письмо"
# Блочная строка (сохраняет переносы, тройные кавычки)
html = """
<div>
#{user}
</div>
"""
Функции
Стрелка -> определяет функцию, => привязывает this.
# Функция: (аргументы) -> тело
square = (x) -> x * x # последнее выражение возвращается
console.log square 5 # 25 (скобки вызова опциональны)
# Многострочная функция — телом служит блок с отступом
greet = (name) ->
msg = "Привет, #{name}"
msg # неявный return
# Аргументы по умолчанию
pow = (base, exp = 2) -> Math.pow base, exp
console.log pow 3 # 9
# Splat ... — переменное число аргументов
sum = (nums...) ->
total = 0
total += n for n in nums
total
console.log sum 1, 2, 3, 4 # 10
# Жирная стрелка => сохраняет контекст this (@)
obj =
items: [1, 2, 3]
show: ->
@items.forEach (x) => # => чтобы @ остался obj
console.log x
Условия
if/else, unless, постфиксная форма, switch/when.
age = 18
if age >= 18
console.log "Совершеннолетний"
else
console.log "Несовершеннолетний"
# unless — это "если НЕ"
unless age >= 18
console.log "Ещё рано"
# Постфиксная (однострочная) форма
console.log "Можно!" if age >= 18
console.log "Стоп" unless age >= 18
# if — это выражение, возвращает значение
status = if age >= 18 then "взрослый" else "ребёнок"
# switch / when (без break, можно несколько значений)
day = "сб"
switch day
when "сб", "вс" then console.log "Выходной"
when "пт" then console.log "Почти выходной"
else console.log "Будний день"
Сравнения и логика
Читаемые операторы is/isnt/and/or/not и проверка существования ?.
a = 5
b = 5
# is → === isnt → !== (строгое сравнение)
console.log a is b # true
console.log a isnt 3 # true
# and → && or → || not → !
console.log (a > 0 and b > 0) # true
console.log (a > 9 or b is 5) # true
console.log (not false) # true
# Оператор существования ? (не null и не undefined)
name = null
console.log name? # false
# Значение по умолчанию, если переменной нет
greeting = name ? "Гость" # "Гость"
# Безопасный доступ ?. — не упадёт на null
user = null
console.log user?.profile?.email # undefined, без ошибки
# Присвоить, только если пусто
config = null
config ?= { theme: "dark" }
Циклы и comprehensions
Циклы — выражения; comprehensions возвращают массив.
# for ... in — перебор элементов массива
for fruit in ["яблоко", "груша", "слива"]
console.log fruit
# С индексом
for item, i in ["a", "b", "c"]
console.log "#{i}: #{item}"
# for ... of — перебор ключей объекта
user = name: "Лена", age: 30
for key, value of user
console.log "#{key} = #{value}"
# while
i = 0
while i < 3
console.log i
i += 1
# List comprehension — собирает результат в массив
squares = (x * x for x in [1..5]) # [1, 4, 9, 16, 25]
# С фильтром through when
evens = (n for n in [1..10] when n % 2 is 0) # [2,4,6,8,10]
# Постфиксный цикл
console.log n for n in [1, 2, 3]
Массивы и объекты
Диапазоны [1..5], лаконичные литералы и деструктуризация.
# Массив (запятые в конце строк можно опускать)
list = [
1
2
3
]
# Диапазоны
range1 = [1..5] # [1, 2, 3, 4, 5] — включительно
range2 = [1...5] # [1, 2, 3, 4] — без последнего
# Срезы массива через диапазон
arr = [10, 20, 30, 40, 50]
console.log arr[1..3] # [20, 30, 40]
# Объект без скобок { } — по отступам
person =
name: "Игорь"
age: 40
city: "Москва"
# Деструктуризация массива
[first, second] = [100, 200] # first=100, second=200
# Деструктуризация объекта
{name, city} = person # name="Игорь", city="Москва"
# Splat в деструктуризации
[head, tail...] = [1, 2, 3, 4] # head=1, tail=[2,3,4]
Классы
class, наследование extends, super, @ как this.
class Animal
# Конструктор. @name — сокращение для this.name
constructor: (@name, @sound = "...") ->
# Метод экземпляра
speak: ->
console.log "#{@name} говорит: #{@sound}"
# Наследование
class Dog extends Animal
constructor: (name) ->
super name, "Гав" # вызов конструктора родителя
# Переопределение метода
speak: ->
super() # вызов родительского speak
console.log "...и виляет хвостом"
rex = new Dog "Рекс"
rex.speak()
# Рекс говорит: Гав
# ...и виляет хвостом
# Статические свойства/методы — через @
class MathUtils
@double: (x) -> x * 2
console.log MathUtils.double 21 # 42
Всё — выражение
Почти любая конструкция возвращает значение, поэтому return редко нужен.
# if возвращает значение
label = if 5 > 3 then "больше" else "меньше"
# switch возвращает значение
grade = (score) ->
switch
when score >= 90 then "A"
when score >= 75 then "B"
else "C"
console.log grade 80 # "B"
# try/catch тоже выражение
result = try
JSON.parse "{ broken"
catch err
"ошибка разбора"
# Функция неявно возвращает последнее выражение
max = (a, b) ->
if a > b then a else b # return не нужен
Компиляция в JavaScript
CoffeeScript не выполняется сам — он переводится в обычный JS.
# Установка компилятора:
# npm install -g coffeescript
#
# Скомпилировать файл в app.js:
# coffee --compile app.coffee
#
# Запустить напрямую (через Node):
# coffee app.coffee
#
# Следить за изменениями и пересобирать:
# coffee --watch --compile app.coffee
# Пример того, во что превращается код.
# CoffeeScript:
square = (x) -> x * x
# Скомпилированный JavaScript:
# var square;
# square = function(x) {
# return x * x;
# };
# Вывод: пишешь чисто на CoffeeScript,
# получаешь читаемый и совместимый JavaScript.