Блог начинающего фронтендера

Шпаргалка по Coffescript

Coffescript — язык программирования, который построен поверх Javascript и компилируется в нативный Javascript. Код комплирируется один в один в соответствующий Javascript, поэтому нет потери во времени выполнения.

Золотое правило Coffescript: "Это всего лишь Javascript".

Официальный сайт

CoffeeScript (далее по тексту - CS) позволяет писать код понятнее и с меньшим количеством ошибок:

  • простой синтаксис с меньшим количеством скобок и запятых;
  • пробелы используются для организации блоков кода;
  • простой синтаксис для выражения функций;

Изначально компилятор был написан на Ruby, позже был реализован на самом Coffeescript.

Текущая реализация Coffeescript 2 компилируется в ES6/ES2015. Значение => из Coffeescript компилируется в => Javascript (далее по тексту - JS), а class из CS в аналогичный class в JS.

Установка

Для работы Coffescript необходимо:

  1. Поставить Node.js
  2. Поставить npm
  3. Выполнить в терминале:
npm install -g coffee-script

Чтобы проверить все ли верно установилось выполняем в терминале (должно вывести help):

coffee -h

Настройка

Создание файла test, который будет компилироваться в обычный js (в реальном проекте проще использовать сборщик проектов):

coffee -c test.coffee

Для компиляции файла каждый раз после сохранения:

coffee -cw test.coffee

Компиляция всех coffee файлов в папке (src) в js:

coffee -c src -o js

Компиляция всех coffee файлов в папке (src) в js сразу после обновления файлов:

coffee -wc src -o js

Чтобы использовать карты исходников нужно передать --map или -m

Плагин для sublime, который при нажатии CMD+B (Mac) показывает во что компилируется coffeescript

Основы

  • Не нужно писать ; в конце. Насколько я понял, можно добавлять если так удобнее (например, по привычке), но лучше все-таки придерживаться чистоты кода
  • Не нужно выделять блоки в фигурные скобки {...} для функций, условий if, switch и try/catch
  • Для функций не обязательно использовать скобки function(), CS будет делать это самостоятельно до конца строки или блока
  • В JS нельзя использовать зарезервированные слова такие как class без кавычек, в CS можно. Он сам следит за такими словами и обертывает если они используются в качестве ключей.

Переменные:

  • Не нужно писать var
$('.account').attr class: 'active'
  log object.class
$('.account').attr({
  "class": "active"
});
log(object["class"]);

Функции:

  • Вместо фигурных скобок -> и отступы
  • Не нужно использовать return. Последняя строка автоматически возвращается как return

Пример объявления функции:

coffee = ->
  confirm "Ready?"
var coffee = function() {
  return confirm("Ready?");
}

Пример работы return:

coffee = ->
  answer = confirm "Ready?"
  "Answer" + answer
  #"Answer #{answer}" - можно так
var coffee;
coffee = function() {
  var answer;
  answer = confirm("Ready?");
  return "Answer" + answer;
}

Пример вызова функции с аргументами:

coffee = (message) ->
  answer = confirm message
  "Answer" + answer
var coffee;
coffee = function(message) {
  var answer;
  answer = confirm(message);
  return "Answer" + answer;
}

Вызов функций:

Вызов функции без аргументов/параметров:

coffee = ->
  coffee()

Вызов функции с одним аргументом/параметром:

coffee = (message) ->
coffee("Yo") или coffee "Yo"

Вызов функции с двумя аргументами/параметрами:

coffee = (message, other) ->
  coffee("Yo", 2) или coffee "Yo", 2

Функция с аргументом по умолчанию:

coffee = (message="Some text") ->
  answer = confirm message
  "Answer" + answer
var coffee;
coffee = function(message) {
  var answer;
  if (message == null) { //в это по идее должно компилиться, 
    message = "Some text"; //но тесты в try и браузере не показывают такой код и компилятся без него
  }
  answer = confirm(message);
  return "Answer" + answer;
}

jQuery в Coffeescript:

На примере табов

jQuery(function($) {
  function changeTab(e) {
    e.preventDefault();
    $("#tabs li a.active").removeClass("active");
    $(this).addClass("active");
  }
  $("#tabs ul li a").click(changeTab);
})
$ -> #вызов функции
  changeTab = (e) -> #вызов функции
    e.preventDefault()
    $("#tabs li a.active").removeClass "active"
    $(@).addClass "active" #для this существует короткая запись @
    $("#tabs ul li a").click changeTab

Условия:

if (age < 18) {
  alert("under age");
}
if age < 18
  alert "under age" #убрали скобки и точки

alert "under age" if age < 18 #другое написание

if age < 18 then alert "under age"  #другое написание с then
if (age < 18) {
  alert("under age");
} else {
  alert("of age");
}
if age < 18
  alert "under age" #убрали скобки и точки
else
  alert("of age") 

alert "under age" if age < 18 #другое написание

if age < 18 then alert "under age" else alert("of age") #другое написание с then, нет тернарных операторов : и ?

Операторы:

CoffescriptJavscript
== is ===
!= inst !==
not !
and &&
or ||
true yes on true
false no off @false

Сравнения по цепочке:

if (2 < newLevel && newLevel < 5) {
  alert("some");
}
if 2 < newLevel < 5
  alert "some"

Операция switch:

var message = (function() {
  switch(cuosOfCoffee) {
    case 0: 
      return "asleep";
    case 1:
      return "eyes open";
    case 2:
      return "buzzed";
    default 
      return "dangerous";
  }
})();
message = switch(cupsOfCoffee) {
  when 0 then "asleep"
  when 1 then "eyes open"
  when 2 then "buzzed"
  else "dangerous"

Проверка на существование:

if (typeof cupsOfCoffee !== "undefined" && cupsOfCoffee !== null) {
  alert('some');
}
if cupsOfCoffee?
  alert "some"

Установить значение равным нулю если оно раньше не было задано:

if not cupsOfCoffee?
  cupsOfCoffee = 0

cupsOfCoffee = 0 unless cupsOfCoffee? #или так

cupsOfCoffee ?= 0 #или так

Вызвать функцию если она существует

if coffeePot?
  coffeePot.brew()

coffeePot?.brew()

Вызвать функцию только если она существует

vehicle.start_engine?().shift_gear?() #Запускает функцию start_engine только если она существует в vehicle и shift_gear только если start_engine существует и запущено

Ряд:

range = [1..4] #две точки

Компилируется в:

var ragne = [1,2,3,4]

С тремя точками:

range = [1...4] #три точки

Компилируется в:

var range = [1,2,3] //исключить последний элемент

Переменные и подмножества:

start = 5
end = 10
range = [start..end] #[5,6,7,8,9,10]

range[1..4] #[6,7,8,9] взять значения с первого по четвертое

range[1..range.length] #[6,7,8,9,10] взять значения с первого до последнего
range[1..-1] #взять значения с первого до последнего

Массив:

array = ['one', 'two', 'three']
#или
array = [
  'one'
  'two'
  'three'
]

Перебор массива:

array.forEach (number, index) ->
  alert "number: #{number}"
#или
for number in array
  alert "number: #{number}"
#или
alert "number: #{number}" for number in array

Изменение массива:

"#number, BIG" for number in array #['one, BIG', 'two, BIG', 'three, BIG']
array = ("#number, BIG" for number in array) #чтобы переписать изначальный массив. скобки очень важны, иначе было бы присвоение

Фильтрация массива:

numberList(number) for number in array when number isnt "three" #выполнить функцию numberList со значниями из массива array кроме значения "three"

Создание нового массива из отфильтрованных значений

newArr = []
for number in array
  newArr.push number if number isnt "three"
#или
newArr = (number for number in array when number isnt "three")

Изменяюшееся количество аргументов

searchLocations = (brand, cities...) ->
  "looking for #{brand} in #{cities.join(', ')}"

searchLocations "H&M", "Moscow"" #looking for H&M in Moscow
searchLocations "H&M", "Moscow", "Sochi" #looking for H&M in Moscow, Sochi
#или
params = ["H&M", "Moscow", "Sochi"]
searchLocations(params...)
awardMedals = (first, second, others...) ->
  gold   = first
  silver = second
  rest   = others

Компилируется в:

var awardMedals, gold, rest, silver, __slice = [].slice;
awardMedals = function() {
  var first, others, second;
  first = arguments[0];
  second = arguments[1]; //про arguments надо еще почитать
  others = 3 <= arguments.length ? __slice.call(arguments, 2) : []; //с этим __slice надо еще разобраться
  gold = first;
  silver = second;
  return rest = others;
};
awardMedals contenders...

Компилируется в:

awardMedals.apply(null, contenders); //про apply надо еще почитать

Объекты:

coffee = {name: "French", strength: 1}
#или 
coffee = name: "French", strength: 1
#или
coffee =
  name: "French"
  strength: 1
coffee =
  name: "French"
  strength: 1
  brew: -> alert("brewing #{@name}") #можно вызвать xthtp coffee.brew()
  pour: (amount=1) ->
    if amount is 1
      "Pour a single cup"
    else 
      "Pourde #{amount} cups"
var coffee = {
  name: "French",
  strength: 1,
  brew: function() {
    return alert("brewing " + this.name);
  }, 
  pour: function(amount) {
    if (amount == null) amount = 1;
    if (amount === 1) {
      return "Pour a single cup"
    } else {
      "Pourde #{amount} cups"
    }
  }
}

Работа с объектами:

coffee = 
  french:
    strength: 1
    in_stock: 20
  italian:
    strength: 2
    in_stock: 12
  defac:
    strength: 0
    in_stock: 0

"#{coffee} has #{attrs.in_stock}" for coffee, attrs of coffees #в массивах используетя in, в объектах of. coffee - key, attrs - value
#результат будет ["french has 20", "italian has 12", "decaf has 0"]

#это аналог:
for coffee, attrs of coffees
  "#{coffee} has #{attrs.in_stock}" 

Фильтрация значений объекта:

to_print = for coffee, attrs of coffees when attrs.in_stock > 0
  "#{coffee} has #{attrs.in_stock}" 
to_print.join ", " #"french has 20", "italian has 12"

Еще примеры упрощения jQuery:

$("#tabs ul li a").bind({
  click: changeTab,
  mouseenter: showNumberOfFlights,
  mouseleave: hideNumberOfFlights,
});
$("#tabs ul li a").bind
  click: changeTab
  mouseenter: showNumberOfFlights
  mouseleave: hideNumberOfFlights
var filterFlights = [];
$.each(currentFlights, function(index, flight) { //какой-то массив currentFlights
  if (stops == "2+" || flight.routing == 0) {
    filterFlights.push(flight);
  }
});
filterFlights = []
$.each currentFlights, (index, flight) -> 
  if stops is "2+" or flight.routing is 0
    filterFlights.push flight
#или
filterFlights =
  (flight for flight in currentFlights when stops is "2+" or flight.routing is 0)

Классы и конструкторы:

coffee = 
  french:
    strength: 1
    in_stock: 20
  italian:
    strength: 2
    in_stock: 12
  defac:
    strength: 0
    in_stock: 0

class Coffee
  constructor: (name, strength=1) ->
    @name = name
    @strength = strength
#или    
class Coffee
  constructor: (@name, @strength=1) ->

Можно добавить функции в этот класс:

class Coffee
  constructor: (@name, @strength=1) ->  
  brew: -> alert("brewing #{@name}") 
  pour: (amount=1) ->
    if amount is 1
      "Poured a single cup"
    else 
      "Poured #{amount} cups"

#создадим новый french
french = new Coffee("French", 2)
french.brew() //alert "brewing French"

Наследование:

class MaxgoodHouse extends Cofeee
  constructor: (@name, @strength=0) ->
    @brand =  "Maxgood House"

boring = new MaxgoodHouse("Boring")
boring.brew() //alert brewing Boring

Можно переписать функцию:

class MaxgoodHouse extends Cofeee
  constructor: (@name, @strength=0) ->
    @brand =  "Maxgood House"
brew: -> alert "Brewing #{@brand} #{@name}"
boring = new MaxgoodHouse("Boring")
boring.brew() #alert brewing Maxgood House Boring

Вызов функции из родительского класса:

class MaxgoodHouse extends Cofeee
  constructor: (@name, @strength=0) ->
    @brand =  "Maxgood House"
  brew: -> alert "Brewing #{@brand} #{@name}"
  pour: (amount=1) ->
    "#{super(amount)}", but it sucks"
boring = new MaxgoodHouse("Boring")
boring.brew() #alert Poured a single cup, but it sucks

Вызов в нужном контексте. Большая стрелка (Fat arrow =>):

class Cofeee
  constructor: (@name, @strength=0, @inventory=0) ->

pourClick: ->
    $("#pour-#{@name}").click (event) =>    #если тут ->,  то
      if @inventory inst 0                     #не будет работать 
        @inventory -= 1                        #потому что будет искать @inventory и @name 
        alert "Poured a cup of #{@name}"       #свойство в dom @ = this.

Еще один пример:

var selectFlights = {
  fetchingFlights: null,

  init: function() {
    $("#tabs ul li a").bind({
      click: this.chageTab
    });

    $("#tabs #error a").click(function (event) {
      e.preventDefault();
      this.showFlights($("tabs li a.active").attr("href"));
    });    
  }

  showFlights: function(activeDiv) {  },
  changeTab: function(event) {  }
});
calss selectFlights
  constructor: (@fetchingFlights=null) ->
    $("#tabs ul li a").bind
      click: @chageTab
    $("#tabs #error a").click (event) =>
      e.preventDefault();
      @showFlights $("tabs li a.active").attr("href")
  showFlights: (activeDiv) ->
  changeTab: (event) =>

На тему Coffescript:

  1. The Little Book on CoffeeScript
  2. http://autotelicum.github.io/Smooth-CoffeeScript/
  3. http://cidocs.ru/coffeescript/
  4. https://coursehunters.net/course/coffee_script

Проверка своего кода:

Поделиться
Отправить
Отправить