CoffeeScript
CoffeeScript — это небольшой язык, который компилируется в JavaScript. Под этой неуклюжей, похожей на Java, оболочкой, JavaScript всегда имел прекрасное ядро. CoffeeScript — это попытка простым способом раскрыть хорошие части JavaScript.
Золотое правило CoffeeScript: «Это просто JavaScript». Код компилируется один в один в эквивалентный JS, и нет интерпретации во время выполнения. Вы можете использовать любую существующую библиотеку JavaScript без проблем из CoffeeScript (и наоборот). Скомпилированный вывод читабельный, форматированный и, как правило, работает так же быстро или быстрее, чем эквивалентный написанный вручную JavaScript.
Последняя версия: 2.7.0
# Install locally for a project: npm install --save-dev coffeescript # Install globally to execute .coffee files anywhere: npm install --global coffeescript
Обзор
CoffeeScript в верхнейлевой части, скомпилированный JavaScript-вывод в нижнейправой части. CoffeeScript можно редактировать!
# Assignment: number = 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list)
// Assignment:
var cubes, list, math, num, number, opposite, race, square;
number = 42;
opposite = true;
if (opposite) {
// Conditions:
number = -42;
}
// Functions:
square = function(x) {
return x * x;
};
// Arrays:
list = [1, 2, 3, 4, 5];
// Objects:
math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};
// Splats:
race = function(winner, ...runners) {
return print(winner, runners);
};
if (typeof elvis !== "undefined" && elvis !== null) {
// Existence:
alert("I knew it!");
}
// Array comprehensions:
cubes = (function() {
var i, len, results;
results = [];
for (i = 0, len = list.length; i < len; i++) {
num = list[i];
results.push(math.cube(num));
}
return results;
})();
CoffeeScript 2
Что нового в CoffeeScript 2?
Самое большое изменение в CoffeeScript 2 заключается в том, что теперь компилятор CoffeeScript генерирует современный синтаксис JavaScript (ES6 или ES2015 и более поздние версии). CoffeeScript => становится JS =>, CoffeeScript class становится JS class и так далее. К основным новым функциям CoffeeScript 2 относятся функции async и JSX. Вы можете прочитать больше в объявлении.
Существует очень мало несовместимых изменений от CoffeeScript 1.x до 2; мы надеемся, что процесс обновления будет гладким для большинства проектов.
Совместимость
Большинство современных функций JavaScript, которые поддерживает CoffeeScript, могут выполняться в Node 7.6+ в родном режиме, что означает, что Node может выполнять вывод CoffeeScript без дополнительной обработки. Вот некоторые заметные исключения:
- JSX всегда требует транспиляции.
- Слэши (splats), т.е. синтаксис rest/spread для объектов, поддерживаются Node 8.6+.
- Флаг регулярных выражений
s(dotall) поддерживается Node 9+. - Асинхронные генераторные функции поддерживаются Node 10+.
-
Модули поддерживаются Node 12+ с
"type": "module"вpackage.jsonвашего проекта.
Этот список может быть неполным и не включает версии Node, которые поддерживают более новые функции с флагами; пожалуйста, обратитесь к node.green для получения полной информации. Вы можете запустить тесты в вашем браузере, чтобы увидеть, какие функции поддерживает ваш браузер. Вы несете ответственность за обеспечение того, что ваша среда выполнения поддерживает используемые вами современные функции; или за то, что вы транспилируете свой код. В случае сомнений, используйте транспиляцию.
Для совместимости с другими JavaScript-фреймворками и инструментами см. Интеграции.
Установка
Командная версия coffee доступна как утилита Node.js, требующая Node 6 или более поздней версии. Однако основной компилятор не зависит от Node и может работать в любой JavaScript-среде или в браузере (см. Попробовать CoffeeScript).
Для установки сначала убедитесь, что у вас есть рабочая копия последней стабильной версии Node.js. Затем вы можете установить CoffeeScript глобально с помощью npm:
npm install --global coffeescript
Это сделает команды coffee и cake доступными глобально.
Если вы используете CoffeeScript в проекте, вы должны установить его локально для этого проекта, чтобы версия CoffeeScript отслеживалась как одна из зависимостей вашего проекта. В папке этого проекта:
npm install --save-dev coffeescript
Команды coffee и cake сначала будут искать в текущей папке установленный локально CoffeeScript и использовать эту версию, если она есть. Это позволяет устанавливать различные версии CoffeeScript глобально и локально.
Если вы планируете использовать опцию --transpile (см. Транспиляция), вам также необходимо установить @babel/core — глобально или локально в зависимости от того, используете ли вы глобальную или локальную версию CoffeeScript.
Использование
Командная строка
После установки у вас должен быть доступ к команде coffee, которая может запускать скрипты, компилировать файлы .coffee в .js, и предоставлять интерактивную оболочку REPL. Команда coffee принимает следующие параметры:
| Параметр | Описание |
|---|---|
-c, --compile | Компилирует скрипт .coffee в файл JavaScript .js того же имени. |
-t, --transpile | Перенаправляет вывод компилятора CoffeeScript через Babel перед сохранением или запуском сгенерированного JavaScript. Требуется установка @babel/core, а параметры для передачи в Babel указаны в файле .babelrc или в package.json с ключом babel в пути к файлу или папке, подлежащей компиляции. См. Транспиляция. |
-m, --map | Генерирует карты исходных кодов вместе со скомпилированными файлами JavaScript. Добавляет директивы sourceMappingURL в JavaScript также. |
-M, --inline-map | То же, что и --map, но включает карту исходных кодов непосредственно в скомпилированные файлы JavaScript, а не в отдельный файл. |
-i, --interactive | Запускает интерактивную сессию CoffeeScript для тестирования коротких фрагментов. Идентично вызову coffee без аргументов. |
-o, --output [DIR] | Выводит все скомпилированные файлы JavaScript в указанную директорию. Используйте в сочетании с --compile или --watch. |
-w, --watch | Отслеживает изменения файлов, повторно выполняя указанную команду при обновлении любого файла. |
-p, --print | Вместо записи JavaScript в файл, печатает его непосредственно в stdout. |
-s, --stdio | Передает CoffeeScript в STDIN и получает JavaScript через STDOUT. Подходит для использования с процессами, написанными на других языках. Пример:cat src/cake.coffee | coffee -sc
|
-l, --literate | Разбирает код как Literate CoffeeScript. Вам нужно указать это только при передаче кода непосредственно через stdio или использовании имени файла без расширения. |
-e, --eval | Компилирует и печатает небольшой фрагмент CoffeeScript непосредственно из командной строки. Например:coffee -e "console.log num for num in [10..1]"
|
-r, --require [MODULE] |
require() указанный модуль перед запуском REPL или оценкой кода, заданного с флагом --eval. |
-b, --bare | Компилирует JavaScript без обертки защиты функций верхнего уровня. |
--no-header | Исключает заголовок «Сгенерировано CoffeeScript». |
--nodejs | У исполняемого файла node есть полезные опции, такие как --debug, --debug-brk, --max-stack-size, и --expose-gc. Используйте этот флаг для передачи опций непосредственно в Node.js. Для передачи нескольких флагов используйте --nodejs несколько раз. |
--ast | Генерирует абстрактное синтаксическое дерево узлов CoffeeScript. Используется для интеграции с JavaScript-инструментами сборки. |
--tokens | Вместо разбора CoffeeScript, просто лексически анализирует его и выводит поток маркеров. Используется для отладки компилятора. |
-n, --nodes | Вместо компиляции CoffeeScript, просто лексически анализирует и разбирает его, и выводит дерево разбора. Используется для отладки компилятора. |
Примеры:
- Компилирует древовидную структуру файлов
.coffeeвsrcв параллельную структуру файлов.jsвlib:
coffee --compile --output lib/ src/ - Отслеживает изменения файла и повторно компилирует его каждый раз при сохранении:
coffee --watch --compile experimental.coffee - Объединяет список файлов в один скрипт:
coffee --join project.js --compile src/*.coffee - Выводит скомпилированный JS из одной строки:
coffee -bpe "alert i for i in [0..10]" - Все вместе, отслеживайте и повторно компилируйте весь проект при работе над ним:
coffee -o lib/ -cw src/ - Запускает CoffeeScript REPL (
Ctrl-Dдля выхода,Ctrl-Vдля многострочных):
coffee
Чтобы использовать --transpile, см. Транспиляция.
Node.js
Если вы хотите использовать CommonJS Node.js для require файлы CoffeeScript, например require './app.coffee', вы должны сначала «зарегистрировать» CoffeeScript как расширение:
require 'coffeescript/register' App = require './app' # The .coffee extension is optional
Если вы хотите использовать API компилятора, например, для создания приложения, которое компилирует строки CoffeeScript на лету, вы можете require весь модуль:
CoffeeScript = require 'coffeescript'
eval CoffeeScript.compile 'console.log "Mmmmm, I could really go for some #{Math.pi}"'
Метод compile имеет сигнатуру compile(code, options), где code — это строка кода CoffeeScript, а необязательный options — это объект с некоторыми или всеми из следующих свойств:
-
options.sourceMap, boolean: если true, генерируется карта исходных кодов; и вместо возврата строки,compileвернет объект вида{js, v3SourceMap, sourceMap}. -
options.inlineMap, boolean: если true, карта исходных кодов выводится как строка, закодированная в base64, в комментарии внизу. -
options.filename, string: имя файла для карты исходных кодов. Оно может включать путь (относительный или абсолютный). -
options.bare, boolean: если true, вывод без обертки защиты функций верхнего уровня. -
options.header, boolean: если true, вывод заголовкаGenerated by CoffeeScript. -
options.transpile, объект: если задано, это должен быть объект с параметрами для передачи в Babel. См. Транспиляция. -
options.ast, boolean: если true, возвращается абстрактное синтаксическое дерево входного кода CoffeeScript.
Транспиляция
CoffeeScript 2 генерирует JavaScript, использующий новейший, современный синтаксис. Интерпретатор или браузер, в котором вы хотите запустить свой код, может не поддерживать весь этот синтаксис. В этом случае мы хотим преобразовать современный JavaScript в более старый JavaScript, который будет работать в более старых версиях Node или браузерах; например, { a } = obj в a = obj.a. Это делается с помощью транспайлеров, таких как Babel, Bublé или Traceur Compiler. Смотрите Инструменты сборки.
Быстрый старт
Из корня вашего проекта:
npm install --save-dev @babel/core @babel/preset-env
echo '{ "presets": ["@babel/env"] }' > .babelrc
coffee --compile --transpile --inline-map some-file.coffee
Транспиляция с помощью компилятора CoffeeScript
Для удобства, CoffeeScript имеет встроенную поддержку популярного транспайлера Babel. Его можно использовать через опцию командной строки --transpile или опцию API Node transpile. Для использования любой из них, @babel/core должен быть установлен в вашем проекте:
npm install --save-dev @babel/core
Или, если вы выполняете команду coffee вне папки проекта, используя глобально установленный модуль coffeescript, @babel/core необходимо установить глобально:
npm install --global @babel/core
По умолчанию Babel ничего не делает — он не делает предположений о том, что вы хотите транспилировать. Вам необходимо предоставить ему конфигурацию, чтобы он знал, что делать. Один из способов сделать это — создать файл .babelrc в папке, содержащей файлы, которые вы компилируете, или в любой родительской папке выше этих файлов. (Babel также поддерживает другие способы.) Минимальный файл .babelrc будет просто { "presets": ["@babel/env"] }. Это подразумевает, что вы установили @babel/preset-env:
npm install --save-dev @babel/preset-env # Or --global for non-project-based usage
Смотрите веб-сайт Babel, чтобы узнать о пресетах и плагинах, и о множестве имеющихся у вас опций. Ещё один пресет, который может вам понадобиться, это @babel/plugin-transform-react-jsx, если вы используете JSX с React (JSX также может использоваться с другими фреймворками).
После того, как у вас установлены @babel/core и @babel/preset-env (или другие пресеты или плагины), а также файл .babelrc (или другой эквивалент), вы можете использовать coffee --transpile для передачи выходных данных CoffeeScript через Babel с использованием сохраненных вами опций.
Если вы используете CoffeeScript через API Node, где вы вызываете CoffeeScript.compile со строкой, подлежащей компиляции, и объектом options, ключ transpile объекта options должен содержать опции Babel:
CoffeeScript.compile(code, {transpile: {presets: ['@babel/env']}})
Вы также можете транспилировать вывод CoffeeScript без использования опции transpile, например, в качестве части цепочки сборки. Это позволяет использовать другие транспайлеры, кроме Babel, и предоставляет больший контроль над процессом. Существует много отличных задач менеджеров для настройки цепочек сборки JavaScript, таких как Gulp, Webpack, Grunt и Broccoli.
Полифилы
Обратите внимание, что транспиляция автоматически не предоставляет полифилы для вашего кода. Сам CoffeeScript выведет Array.indexOf, если вы используете оператор in, или синтаксис деструктуризации или spread/rest; и Function.bind, если вы используете связанный (=>) метод в классе. Оба поддерживаются в Internet Explorer 9+ и всех более новых браузерах, но вам нужно предоставить полифилы, если вам нужно поддерживать Internet Explorer 8 или ниже и вы используете функции, которые могут привести к выводу этих методов. Вам также необходимо предоставить полифилы, если ваш собственный код использует эти методы или другой метод, добавленный в последних версиях JavaScript. Один из вариантов полифилов — @babel/polyfill, хотя существует много других стратегий.
Справочник по языку
Эта справка структурирована таким образом, что её можно читать сверху вниз, если вам нравится. Более поздние разделы используют идеи и синтаксис, представленные ранее. Предполагается знание JavaScript. Во всех следующих примерах исходный CoffeeScript приводится слева, а непосредственная компиляция в JavaScript — справа.
Многие примеры можно выполнить (где это имеет смысл), нажав кнопку ▶ справа. CoffeeScript слева можно редактировать, и JavaScript будет обновляться по мере редактирования.
Сначала основы: CoffeeScript использует значимые отступы для разграничения блоков кода. Вам не нужно использовать точки с запятой ; для завершения выражений, завершение строки сделает то же самое (хотя точки с запятой всё ещё могут использоваться для размещения нескольких выражений в одной строке). Вместо использования фигурных скобок { } для заключения блоков кода в функциях, условных операторах if, switch и try/catch, используйте отступы.
Вам не нужно использовать скобки для вызова функции, если вы передаёте аргументы. Неявный вызов продолжается до конца строки или блока выражения.
console.log sys.inspect object → console.log(sys.inspect(object));
Функции
Функции определяются с помощью необязательного списка параметров в скобках, стрелки и тела функции. Пустая функция выглядит так: ->
square = (x) -> x * x cube = (x) -> square(x) * x
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
Функции также могут иметь значения по умолчанию для аргументов, которые будут использоваться, если аргумент отсутствует (undefined).
fill = (container, liquid = "coffee") ->
"Filling the #{container} with #{liquid}..."
var fill;
fill = function(container, liquid = "coffee") {
return `Filling the ${container} with ${liquid}...`;
};
Строки
Как и в JavaScript и во многих других языках, CoffeeScript поддерживает строки, ограниченные символами " или '. CoffeeScript также поддерживает интерполяцию строк в строках с двойными кавычками, используя ". Строки в одинарных кавычках являются литеральными. Вы даже можете использовать интерполяцию в ключах объектов.
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author }"
sentence = "#{ 22 / 7 } is a decent approximation of π"
var author, quote, sentence;
author = "Wittgenstein";
quote = `A picture is a fact. -- ${author}`;
sentence = `${22 / 7} is a decent approximation of π`;
В CoffeeScript разрешены многострочные строки. Строки соединяются одним пробелом, если они не заканчиваются обратной косой чертой. Отступы игнорируются.
mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world..."
var mobyDick; mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
Строки блоков, ограниченные """ или ''', могут использоваться для хранения форматированного или чувствительного к отступам текста (или, если вы просто не хотите экранировать кавычки и апострофы). Уровень отступа, который начинается с блока, сохраняется на протяжении всего, поэтому вы можете сохранить всё выровненным с телом вашего кода.
html = """
<strong>
cup of coffeescript
</strong>
"""
var html; html = `<strong> cup of coffeescript </strong>`;
Строки блоков с двойными кавычками, как и другие строки с двойными кавычками, позволяют интерполяцию.
Объекты и массивы
Литералы CoffeeScript для объектов и массивов очень похожи на своих JavaScript-аналогов. Когда каждый свойство перечислен в отдельной строке, запятые необязательны. Объекты могут быть созданы с помощью отступов вместо явных фигур, что напоминает YAML.
song = ["do", "re", "mi", "fa", "so"]
singers = {Jagger: "Rock", Elvis: "Roll"}
bitlist = [
1, 0, 1
0, 0, 1
1, 1, 0
]
kids =
brother:
name: "Max"
age: 11
sister:
name: "Ida"
age: 9
var bitlist, kids, singers, song;
song = ["do", "re", "mi", "fa", "so"];
singers = {
Jagger: "Rock",
Elvis: "Roll"
};
bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];
kids = {
brother: {
name: "Max",
age: 11
},
sister: {
name: "Ida",
age: 9
}
};
CoffeeScript имеет сокращение для создания объектов, когда вы хотите установить ключ с помощью переменной с тем же именем. Обратите внимание, что { и } являются обязательными для этого сокращения.
name = "Michelangelo"
mask = "orange"
weapon = "nunchuks"
turtle = {name, mask, weapon}
output = "#{turtle.name} wears an #{turtle.mask} mask. Watch out for his #{turtle.weapon}!"
var mask, name, output, turtle, weapon;
name = "Michelangelo";
mask = "orange";
weapon = "nunchuks";
turtle = {name, mask, weapon};
output = `${turtle.name} wears an ${turtle.mask} mask. Watch out for his ${turtle.weapon}!`;
Комментарии
В CoffeeScript комментарии обозначаются символом # до конца строки или от ### до следующего появления ###. Компилятор игнорирует комментарии, хотя он делает всё возможное, чтобы вставить ваши комментарии в выходной JavaScript после компиляции.
### Fortune Cookie Reader v1.0 Released under the MIT License ### sayFortune = (fortune) -> console.log fortune # in bed!
/*
Fortune Cookie Reader v1.0
Released under the MIT License
*/
var sayFortune;
sayFortune = function(fortune) {
return console.log(fortune); // in bed!
};
Встроенные ### комментарии делают возможными аннотации типов.
Лексическая область видимости и безопасность переменных
Компилятор CoffeeScript заботится о том, чтобы все ваши переменные были должным образом объявлены в лексической области видимости — вам никогда не нужно писать var самостоятельно.
outer = 1 changeNumbers = -> inner = -1 outer = 10 inner = changeNumbers()
var changeNumbers, inner, outer;
outer = 1;
changeNumbers = function() {
var inner;
inner = -1;
return outer = 10;
};
inner = changeNumbers();
Обратите внимание, как все объявления переменных были подняты вверх до ближайшего уровня области видимости, в первый раз, когда они появляются. outer не объявляется заново внутри внутренней функции, потому что она уже находится в области видимости; inner внутри функции, с другой стороны, не должна изменять значение внешней переменной с тем же именем и, следовательно, имеет своё собственное объявление.
Поскольку у вас нет прямого доступа к ключевому слову var, невозможно намеренно затенять внешнюю переменную, вы можете только ссылаться на неё. Поэтому будьте осторожны, чтобы случайно не повторно использовать имя внешней переменной, если вы пишете глубоко вложенную функцию.
Хотя в этом документе это подавлено для ясности, весь вывод CoffeeScript (кроме файлов со строками import или export) заключён в анонимную функцию: (function(){ … })();. Этот защитный обертка, в сочетании с автоматическим генерированием ключевого слова var, делает чрезвычайно сложным случайное загрязнение глобального пространства имён. (Защитный обертка может быть отключен с помощью опции bare, и является ненужным и автоматически отключается при использовании модулей.)
Если вы хотите создать переменные верхнего уровня для использования другими скриптами, прикрепите их в качестве свойств к window; прикрепите их как свойства к объекту exports в CommonJS; или используйте инструкцию export. Если вы хотите нацеливаться как на CommonJS, так и на браузер, оператор существования (описанный ниже) даёт вам надёжный способ определить, где добавить их: exports ? this.
Поскольку CoffeeScript заботится обо всех объявлениях переменных, объявить переменные с помощью ES2015 let или const невозможно. Это сделано намеренно; мы считаем, что простота, получаемая от того, что не нужно думать об объявлении переменных, перевешивает преимущества наличия трёх различных способов объявления переменных.
Условные операторы: if, else, unless и условное присваивание
Операторы if, else, unless и условного присваивания могут быть записаны без использования скобок и фигурных скобок. Как и функции и другие блочные выражения, многострочные условные операторы разделяются отступами. Также существует удобный постфиксный формат с окончанием if или unless.
CoffeeScript может компилировать операторы if, else, unless и условного присваивания в выражения JavaScript, используя оператор тернарного условия, когда это возможно, и обёртку closure в противном случае. В CoffeeScript нет явного оператора тернарного условия — вы просто используете обычный оператор if в одну строку.
mood = greatlyImproved if singing if happy and knowsIt clapsHands() chaChaCha() else showIt() date = if friday then sue else jill
var date, mood;
if (singing) {
mood = greatlyImproved;
}
if (happy && knowsIt) {
clapsHands();
chaChaCha();
} else {
showIt();
}
date = friday ? sue : jill;
Разброс параметров, или синтаксис rest/spread
Объект JavaScript arguments является полезным инструментом для работы с функциями, принимающими переменное количество аргументов. CoffeeScript предоставляет разброс ..., как для определения функции, так и для вызова, что делает работу с переменным количеством аргументов немного более удобной. ES2015 принял эту функцию в качестве параметров rest.
gold = silver = rest = "unknown"
awardMedals = (first, second, others...) ->
gold = first
silver = second
rest = others
contenders = [
"Michael Phelps"
"Liu Xiang"
"Yao Ming"
"Allyson Felix"
"Shawn Johnson"
"Roman Sebrle"
"Guo Jingjing"
"Tyson Gay"
"Asafa Powell"
"Usain Bolt"
]
awardMedals contenders...
alert """
Gold: #{gold}
Silver: #{silver}
The Field: #{rest.join ', '}
"""
var awardMedals, contenders, gold, rest, silver;
gold = silver = rest = "unknown";
awardMedals = function(first, second, ...others) {
gold = first;
silver = second;
return rest = others;
};
contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];
awardMedals(...contenders);
alert(`Gold: ${gold}
Silver: ${silver}
The Field: ${rest.join(', ')}`);
Разброс также позволяет нам опустить элементы массива…
popular = ['pepperoni', 'sausage', 'cheese'] unwanted = ['anchovies', 'olives'] all = [popular..., unwanted..., 'mushrooms']
var all, popular, unwanted; popular = ['pepperoni', 'sausage', 'cheese']; unwanted = ['anchovies', 'olives']; all = [...popular, ...unwanted, 'mushrooms'];
…и свойства объекта.
user =
name: 'Werner Heisenberg'
occupation: 'theoretical physicist'
currentUser = { user..., status: 'Uncertain' }
var currentUser, user;
user = {
name: 'Werner Heisenberg',
occupation: 'theoretical physicist'
};
currentUser = {
...user,
status: 'Uncertain'
};
В ECMAScript это называется синтаксисом spread, и поддерживается для массивов с ES2015, а для объектов — с ES2018.
Циклы и выражения-генерации
Большинство циклов, которые вы напишете на CoffeeScript, будут являться выражениями-генерации над массивами, объектами и диапазонами. Выражения-генерации заменяют (и компилируются в) for циклы с необязательными условиями и значением текущего индекса массива. В отличие от циклов for, выражения-генерации для массивов являются выражениями и могут возвращаться и присваиваться.
# Eat lunch.
eat = (food) -> "#{food} eaten."
eat food for food in ['toast', 'cheese', 'wine']
# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu = (i, dish) -> "Menu Item #{i}: #{dish}"
menu i + 1, dish for dish, i in courses
# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
// Eat lunch.
var courses, dish, eat, food, foods, i, j, k, l, len, len1, len2, menu, ref;
eat = function(food) {
return `${food} eaten.`;
};
ref = ['toast', 'cheese', 'wine'];
for (j = 0, len = ref.length; j < len; j++) {
food = ref[j];
eat(food);
}
// Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];
menu = function(i, dish) {
return `Menu Item ${i}: ${dish}`;
};
for (i = k = 0, len1 = courses.length; k < len1; i = ++k) {
dish = courses[i];
menu(i + 1, dish);
}
// Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate'];
for (l = 0, len2 = foods.length; l < len2; l++) {
food = foods[l];
if (food !== 'chocolate') {
eat(food);
}
}
Выражения-генерации должны справляться с большинством мест, где вы в противном случае использовали бы цикл, each/forEach, map, или select/filter, например:
shortNames = (name for name in list when name.length < 5)
Если вы знаете начало и конец цикла или хотите выполнять шаги с фиксированными размерами, вы можете использовать диапазон для указания начала и конца выражения-генерации.
countdown = (num for num in [10..1])
var countdown, num;
countdown = (function() {
var i, results;
results = [];
for (num = i = 10; i >= 1; num = --i) {
results.push(num);
}
return results;
})();
Обратите внимание, как потому что мы присваиваем значение выражения-генерации переменной в приведённом выше примере, CoffeeScript собирает результат каждой итерации в массив. Иногда функции заканчиваются циклами, которые предназначены для выполнения только побочных эффектов. Будьте осторожны, чтобы не возвращать случайно результаты выражения-генерации в этих случаях, добавив осмысленное возвращаемое значение — например, true — или null, в конец вашей функции.
Чтобы перебирать диапазон выражения-генерации в фиксированных размерах, используйте by, например: evens = (x for x in [0..10] by 2)
Если вам не нужно текущее значение итерации, вы можете его опустить: browser.closeCurrentTab() for [0...count]
Выражения-генерации также могут использоваться для перебора ключей и значений в объекте. Используйте of для обозначения выражения-генерации над свойствами объекта вместо значений в массиве.
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
var age, ages, child, yearsOld;
yearsOld = {
max: 10,
ida: 9,
tim: 11
};
ages = (function() {
var results;
results = [];
for (child in yearsOld) {
age = yearsOld[child];
results.push(`${child} is ${age}`);
}
return results;
})();
Если вы хотите перебирать только те ключи, которые определены в самом объекте, добавив hasOwnProperty проверку, чтобы избежать свойств, унаследованных от прототипа, используйте for own key, value of object.
Чтобы перебирать функцию-генератор, используйте from. См. Функции-генераторы.
Единственный низкоуровневый цикл, предоставляемый CoffeeScript, — это цикл while. Основное различие с JavaScript заключается в том, что цикл while может использоваться как выражение, возвращая массив, содержащий результат каждой итерации цикла.
# Econ 101
if this.studyingEconomics
buy() while supply > demand
sell() until supply > demand
# Nursery Rhyme
num = 6
lyrics = while num -= 1
"#{num} little monkeys, jumping on the bed.
One fell out and bumped his head."
// Econ 101
var lyrics, num;
if (this.studyingEconomics) {
while (supply > demand) {
buy();
}
while (!(supply > demand)) {
sell();
}
}
// Nursery Rhyme
num = 6;
lyrics = (function() {
var results;
results = [];
while (num -= 1) {
results.push(`${num} little monkeys, jumping on the bed. One fell out and bumped his head.`);
}
return results;
})();
Для повышения читабельности ключевое слово until эквивалентно while not, а ключевое слово loop эквивалентно while true.
При использовании цикла JavaScript для генерации функций часто вставляют обёртку closure для обеспечения того, что переменные цикла закрываются, и все сгенерированные функции не просто делят конечные значения. CoffeeScript предоставляет ключевое слово do, которое немедленно вызывает переданную функцию, передавая любые аргументы.
for filename in list
do (filename) ->
if filename not in ['.DS_Store', 'Thumbs.db', 'ehthumbs.db']
fs.readFile filename, (err, contents) ->
compile filename, contents.toString()
var filename, i, len;
for (i = 0, len = list.length; i < len; i++) {
filename = list[i];
(function(filename) {
if (filename !== '.DS_Store' && filename !== 'Thumbs.db' && filename !== 'ehthumbs.db') {
return fs.readFile(filename, function(err, contents) {
return compile(filename, contents.toString());
});
}
})(filename);
}
Выделение и вставка массивов с помощью диапазонов
Диапазоны также могут использоваться для извлечения срезов массивов. С двумя точками (3..6) диапазон является включительным (3, 4, 5, 6); с тремя точками (3...6) диапазон исключает конец (3, 4, 5). Индексы срезов имеют полезные значения по умолчанию. Опущенный первый индекс по умолчанию равен нулю, а опущенный второй индекс по умолчанию равен размеру массива.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] start = numbers[0..2] middle = numbers[3...-2] end = numbers[-2..] copy = numbers[..]
var copy, end, middle, numbers, start; numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; start = numbers.slice(0, 3); middle = numbers.slice(3, -2); end = numbers.slice(-2); copy = numbers.slice(0);
Тот же синтаксис может быть использован с присваиванием для замены части массива новыми значениями, вставляя их.
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] numbers[3..6] = [-3, -4, -5, -6]
var numbers, ref, splice = [].splice; numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; splice.apply(numbers, [3, 4].concat(ref = [-3, -4, -5, -6])), ref;
Обратите внимание, что строки JavaScript неизменяемы и не могут быть вставлены.
Всё — выражение (по крайней мере, насколько это возможно)
Вы, возможно, заметили, что даже если мы не добавляем операторы return в функции CoffeeScript, они тем не менее возвращают своё конечное значение. Компилятор CoffeeScript старается убедиться, что все операторы языка могут использоваться как выражения. Посмотрите, как return переносится в каждую возможную ветвь выполнения в функции ниже.
grade = (student) ->
if student.excellentWork
"A+"
else if student.okayStuff
if student.triedHard then "B" else "B-"
else
"C"
eldest = if 24 > 21 then "Liz" else "Ike"
var eldest, grade;
grade = function(student) {
if (student.excellentWork) {
return "A+";
} else if (student.okayStuff) {
if (student.triedHard) {
return "B";
} else {
return "B-";
}
} else {
return "C";
}
};
eldest = 24 > 21 ? "Liz" : "Ike";
Несмотря на то, что функции всегда возвращают своё конечное значение, возможно и рекомендуется возвращать управление из тела функции, написав явный оператор return (return value), когда вы знаете, что закончили.
Поскольку объявления переменных происходят в начале области видимости, присваивание может использоваться в выражениях, даже для переменных, которые ещё не были встречены:
six = (one = 1) + (two = 2) + (three = 3)
var one, six, three, two; six = (one = 1) + (two = 2) + (three = 3);
Элементы, которые в противном случае были бы операторами в JavaScript, при использовании в качестве части выражения в CoffeeScript преобразуются в выражения путём обертывания их в closure. Это позволяет выполнять полезные действия, например, присваивать результат выражения-генерации переменной:
# The first ten global properties. globals = (name for name of window)[0...10]
// The first ten global properties.
var globals, name;
globals = ((function() {
var results;
results = [];
for (name in window) {
results.push(name);
}
return results;
})()).slice(0, 10);
А также бессмысленные действия, например, передавать оператор try/catch напрямую в вызов функции:
alert(
try
nonexistent / undefined
catch error
"And the error is ... #{error}"
)
var error;
alert((function() {
try {
return nonexistent / void 0;
} catch (error1) {
error = error1;
return `And the error is ... ${error}`;
}
})());
Есть несколько операторов в JavaScript, которые нельзя осмысленно преобразовать в выражения, а именно break, continue, и return. Если вы используете их в блоке кода, CoffeeScript не будет пытаться выполнить преобразование.
Операторы и псевдонимы
Поскольку оператор == часто приводит к нежелательному преобразованию, не транзитивен и имеет другое значение, чем в других языках, CoffeeScript компилирует == в ===, а != в !==. Кроме того, is компилируется в ===, а isnt в !==.
Вы можете использовать not в качестве псевдонима для !.
Для логических операций and компилируется в &&, а or в ||.
Вместо новой строки или точки с запятой, then может использоваться для разделения условий от выражений в операторах while, if/else, и switch/when.
Как и в YAML, on и yes эквивалентны булевым true, в то время как off и no — булевым false.
unless может использоваться в качестве обратного оператора if.
В качестве сокращения для this.property, вы можете использовать @property.
Вы можете использовать in для проверки наличия массива и of для проверки наличия ключа JavaScript-объекта.
В цикле for, from компилируется в ES2015 of. (К сожалению, CoffeeScript of предшествовал ES2015 of.)
Для упрощения математических выражений ** может использоваться для возведения в степень, а // выполняет целочисленное деление. % работает так же, как и в JavaScript, в то время как %% обеспечивает “определяемое делимым модульное” деление:
-7 % 5 == -2 # The remainder of 7 / 5 -7 %% 5 == 3 # n %% 5 is always between 0 and 4 tabs.selectTabAtIndex((tabs.currentIndex - count) %% tabs.length)
var modulo = function(a, b) { return (+a % (b = +b) + b) % b; };
-7 % 5 === -2; // The remainder of 7 / 5
modulo(-7, 5) === 3; // n %% 5 is always between 0 and 4
tabs.selectTabAtIndex(modulo(tabs.currentIndex - count, tabs.length));
Всё вместе:
| CoffeeScript | JavaScript |
|---|---|
is | === |
isnt | !== |
not | ! |
and | && |
or | || |
true, yes, on
| true |
false, no, off | false |
@, this
| this |
a in b | [].indexOf.call(b, a) >= 0 |
a of b | a in b |
for a from b | for (a of b) |
a ** b | a ** b |
a // b | Math.floor(a / b) |
a %% b | (a % b + b) % b |
launch() if ignition is on
volume = 10 if band isnt SpinalTap
letTheWildRumpusBegin() unless answer is no
if car.speed < limit then accelerate()
winner = yes if pick in [47, 92, 13]
print inspect "My name is #{@name}"
var volume, winner;
if (ignition === true) {
launch();
}
if (band !== SpinalTap) {
volume = 10;
}
if (answer !== false) {
letTheWildRumpusBegin();
}
if (car.speed < limit) {
accelerate();
}
if (pick === 47 || pick === 92 || pick === 13) {
winner = true;
}
print(inspect(`My name is ${this.name}`));
Оператор существования
Проверить существование переменной в JavaScript немного сложно. if (variable) … близко к этому, но не работает для нуля, пустой строки и false (упомянув лишь самые распространённые случаи). Оператор существования CoffeeScript ? возвращает true, если переменная не null или undefined или не объявлена, что делает его аналогом оператора nil? в Ruby.
Он также может использоваться для более безопасной условной присваивания, чем обеспечивает шаблон JavaScript a = a || value, в случаях, когда вы можете работать с числами или строками.
solipsism = true if mind? and not world? speed = 0 speed ?= 15 footprints = yeti ? "bear"
var footprints, solipsism, speed;
if ((typeof mind !== "undefined" && mind !== null) && (typeof world === "undefined" || world === null)) {
solipsism = true;
}
speed = 0;
if (speed == null) {
speed = 15;
}
footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";
Обратите внимание, что если компилятор знает, что a находится в области видимости и, следовательно, объявлена, то a? компилируется в a != null, а не в a !== null. Оператор != делает нестрогое сравнение с null, который также выполняет двойную работу, сравнивая и с undefined. Обратное также верно для not a? или unless a?.
major = 'Computer Science' unless major? signUpForClass 'Introduction to Wines'
var major;
major = 'Computer Science';
if (major == null) {
signUpForClass('Introduction to Wines');
}
Если переменная может быть не объявлена, компилятор выполняет тщательную проверку. Вот что программисты на JavaScript должны вводить, когда хотят проверить, существует ли неизвестная переменная.
if window? environment = 'browser (probably)'
var environment;
if (typeof window !== "undefined" && window !== null) {
environment = 'browser (probably)';
}
Вариант доступа к оператору существования ?. можно использовать для обработки ссылок null в цепочке свойств. Используйте его вместо точечного доступа . в случаях, когда базовое значение может быть null или undefined. Если все свойства существуют, вы получите ожидаемый результат; если цепочка прервана, вместо TypeError, которое обычно возникает, возвращается undefined.
zip = lottery.drawWinner?().address?.zipcode
var ref, zip; zip = typeof lottery.drawWinner === "function" ? (ref = lottery.drawWinner().address) != null ? ref.zipcode : void 0 : void 0;
Для полноты:
| Пример | Определение |
|---|---|
a? | проверяет, что a находится в области видимости и a != null
|
a ? b | возвращает a, если a находится в области видимости и a != null; в противном случае, b
|
a?.b или a?['b']
| возвращает a.b, если a находится в области видимости и a != null; в противном случае, undefined
|
a?(b, c) или a? b, c | возвращает результат вызова a (с аргументами b и c) если a находится в области видимости и является вызываемой функцией; в противном случае, undefined
|
a ?= b | присваивает значение b переменной a если a не находится в области видимости или если a == null; производит новое значение a
|
Цепочки вызовов функций
Ведущий . закрывает все открытые вызовы, что позволяет упростить синтаксис цепочки.
$ 'body' .click (e) -> $ '.box' .fadeIn 'fast' .addClass 'show' .css 'background', 'white'
$('body').click(function(e) {
return $('.box').fadeIn('fast').addClass('show');
}).css('background', 'white');
Деструктурирующее присваивание
Так же, как и в JavaScript (с ES2015), CoffeeScript имеет синтаксис деструктурирующего присваивания. Когда вы присваиваете массив или литерал объекта значению, CoffeeScript разбивает и сопоставляет обе стороны друг с другом, присваивая значения справа переменным слева. В простейшем случае он может использоваться для параллельного присваивания:
theBait = 1000 theSwitch = 0 [theBait, theSwitch] = [theSwitch, theBait]
var theBait, theSwitch; theBait = 1000; theSwitch = 0; [theBait, theSwitch] = [theSwitch, theBait];
Но он также полезен для работы с функциями, возвращающими несколько значений.
weatherReport = (location) -> # Make an Ajax request to fetch the weather... [location, 72, "Mostly Sunny"] [city, temp, forecast] = weatherReport "Berkeley, CA"
var city, forecast, temp, weatherReport;
weatherReport = function(location) {
// Make an Ajax request to fetch the weather...
return [location, 72, "Mostly Sunny"];
};
[city, temp, forecast] = weatherReport("Berkeley, CA");
Деструктурирующее присваивание может использоваться с любым уровнем вложенности массивов и объектов, чтобы извлечь глубоко вложенные свойства.
futurists =
sculptor: "Umberto Boccioni"
painter: "Vladimir Burliuk"
poet:
name: "F.T. Marinetti"
address: [
"Via Roma 42R"
"Bellagio, Italy 22021"
]
{sculptor} = futurists
{poet: {name, address: [street, city]}} = futurists
var city, futurists, name, sculptor, street;
futurists = {
sculptor: "Umberto Boccioni",
painter: "Vladimir Burliuk",
poet: {
name: "F.T. Marinetti",
address: ["Via Roma 42R", "Bellagio, Italy 22021"]
}
};
({sculptor} = futurists);
({
poet: {
name,
address: [street, city]
}
} = futurists);
Деструктурирующее присваивание может даже сочетаться со слэшами.
tag = "<impossible>"
[open, contents..., close] = tag.split("")
var close, contents, open, ref, tag,
splice = [].splice;
tag = "<impossible>";
ref = tag.split(""), [open, ...contents] = ref, [close] = splice.call(contents, -1);
Расширение можно использовать для извлечения элементов из конца массива, не присваивая при этом остальные его значения. Оно также работает в списках параметров функций.
text = "Every literary critic believes he will
outwit history and have the last word"
[first, ..., last] = text.split " "
var first, last, ref, text,
slice = [].slice;
text = "Every literary critic believes he will outwit history and have the last word";
ref = text.split(" "), [first] = ref, [last] = slice.call(ref, -1);
Деструктурирующее присваивание также полезно при сочетании с конструкторами классов для присваивания свойств экземпляру из объекта параметров, переданного конструктору.
class Person
constructor: (options) ->
{@name, @age, @height = 'average'} = options
tim = new Person name: 'Tim', age: 4
var Person, tim;
Person = class Person {
constructor(options) {
({name: this.name, age: this.age, height: this.height = 'average'} = options);
}
};
tim = new Person({
name: 'Tim',
age: 4
});
Приведённый выше пример также демонстрирует, что если свойства отсутствуют в деструктурируемом объекте или массиве, вы можете, как и в JavaScript, указать значения по умолчанию. Однако в отличие от оператора существования, значение по умолчанию применяется только если значение отсутствует или undefined—передача null установит значение null, а не значение по умолчанию.
Связанные (стрелочные) функции
В JavaScript ключевое слово this динамически связано со значением объекта, к которому прикреплена текущая функция. Если вы передаёте функцию как обратный вызов или прикрепляете её к другому объекту, исходное значение this будет утеряно. Если вы с этим не знакомы, статья Digital Web даёт хороший обзор особенностей.
Стрелочная функция => может использоваться как для определения функции, так и для связывания её с текущим значением this, прямо на месте. Это полезно при использовании библиотек на основе обратных вызовов, таких как Prototype или jQuery, для создания функций-итераторов для передачи each или функций-обработчиков событий для использования с on. Функции, созданные с помощью стрелочной функции, могут получить доступ к свойствам объекта this, в котором они определены.
Account = (customer, cart) ->
@customer = customer
@cart = cart
$('.shopping_cart').on 'click', (event) =>
@customer.purchase @cart
var Account;
Account = function(customer, cart) {
this.customer = customer;
this.cart = cart;
return $('.shopping_cart').on('click', (event) => {
return this.customer.purchase(this.cart);
});
};
Если бы мы использовали -> в обратном вызове выше, @customer бы ссылался на неопределённое свойство «customer» элемента DOM, и попытка вызвать purchase() на нём привела бы к исключению.
Стрелочная функция была одной из самых популярных функций CoffeeScript, и ES2015 приняла её; таким образом, CoffeeScript 2 компилирует => в ES =>.
Генераторные функции
CoffeeScript поддерживает генераторные функции ES2015 через ключевое слово yield. Нет бессмысленной конструкции function*(){}; генератор в CoffeeScript — это просто функция, которая возвращает значение.
perfectSquares = ->
num = 0
loop
num += 1
yield num * num
return
window.ps or= perfectSquares()
var perfectSquares;
perfectSquares = function*() {
var num;
num = 0;
while (true) {
num += 1;
yield num * num;
}
};
window.ps || (window.ps = perfectSquares());
yield* называется yield from, и yield return может использоваться, если нужно принудительно использовать генератор, который не возвращает значение.
Вы можете итерироваться по генераторной функции, используя for…from.
fibonacci = ->
[previous, current] = [1, 1]
loop
[previous, current] = [current, previous + current]
yield current
return
getFibonacciNumbers = (length) ->
results = [1]
for n from fibonacci()
results.push n
break if results.length is length
results
var fibonacci, getFibonacciNumbers;
fibonacci = function*() {
var current, previous;
[previous, current] = [1, 1];
while (true) {
[previous, current] = [current, previous + current];
yield current;
}
};
getFibonacciNumbers = function(length) {
var n, results;
results = [1];
for (n of fibonacci()) {
results.push(n);
if (results.length === length) {
break;
}
}
return results;
};
Асинхронные функции
Асинхронные функции ES2017 поддерживаются через ключевое слово await. Как и с генераторами, нет необходимости в ключевом слове async; асинхронная функция в CoffeeScript — это просто функция, которая ожидает.
Так же, как yield return принудительно использует генератор, await return может использоваться для принудительного преобразования функции в асинхронную.
# Your browser must support async/await and speech synthesis
# to run this example.
sleep = (ms) ->
new Promise (resolve) ->
window.setTimeout resolve, ms
say = (text) ->
window.speechSynthesis.cancel()
window.speechSynthesis.speak new SpeechSynthesisUtterance text
countdown = (seconds) ->
for i in [seconds..1]
say i
await sleep 1000 # wait one second
say "Blastoff!"
countdown 3
// Your browser must support async/await and speech synthesis
// to run this example.
var countdown, say, sleep;
sleep = function(ms) {
return new Promise(function(resolve) {
return window.setTimeout(resolve, ms);
});
};
say = function(text) {
window.speechSynthesis.cancel();
return window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
};
countdown = async function(seconds) {
var i, j, ref;
for (i = j = ref = seconds; (ref <= 1 ? j <= 1 : j >= 1); i = ref <= 1 ? ++j : --j) {
say(i);
await sleep(1000); // wait one second
}
return say("Blastoff!");
};
countdown(3);
Классы
CoffeeScript 1 предоставил ключевые слова class и extends в качестве синтаксического сахара для работы с прототипическими функциями. С ES2015 JavaScript принял эти ключевые слова; поэтому CoffeeScript 2 компилирует свои ключевые слова class и extends в классы ES2015.
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()
var Animal, Horse, Snake, sam, tom;
Animal = class Animal {
constructor(name) {
this.name = name;
}
move(meters) {
return alert(this.name + ` moved ${meters}m.`);
}
};
Snake = class Snake extends Animal {
move() {
alert("Slithering...");
return super.move(5);
}
};
Horse = class Horse extends Animal {
move() {
alert("Galloping...");
return super.move(45);
}
};
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();
Статические методы можно определять, используя @ перед именем метода:
class Teenager
@say: (speech) ->
words = speech.split ' '
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe']
output = []
for word, index in words
output.push word
output.push fillers[Math.floor(Math.random() * fillers.length)] unless index is words.length - 1
output.join ', '
var Teenager;
Teenager = class Teenager {
static say(speech) {
var fillers, i, index, len, output, word, words;
words = speech.split(' ');
fillers = ['uh', 'um', 'like', 'actually', 'so', 'maybe'];
output = [];
for (index = i = 0, len = words.length; i < len; index = ++i) {
word = words[index];
output.push(word);
if (index !== words.length - 1) {
output.push(fillers[Math.floor(Math.random() * fillers.length)]);
}
}
return output.join(', ');
}
};
Наконец, определения классов — это блоки исполняемого кода, которые позволяют интересными возможностями метапрограммирования. В контексте определения класса, this — это сам объект класса; поэтому вы можете присвоить статические свойства, используя @property: value.
Наследование прототипов
Помимо поддержки классов ES2015, CoffeeScript предоставляет сокращённый способ работы с прототипами. Оператор :: даёт вам быстрый доступ к прототипу объекта:
String::dasherize = -> this.replace /_/g, "-"
String.prototype.dasherize = function() {
return this.replace(/_/g, "-");
};
Переключатель/Когда/Иначе
Инструкции switch в JavaScript немного неудобны. Вам нужно помнить, что необходимо break в конце каждой инструкции case для предотвращения случайного перехода к варианту по умолчанию. CoffeeScript предотвращает случайный переход и может преобразовать switch в возвращаемое, присваиваемое выражение. Формат: switch условие, when пункты, else вариант по умолчанию.
Как и в Ruby, инструкции switch в CoffeeScript могут принимать несколько значений для каждого when пункта. Если какое-либо из значений совпадает, выполняется пункт.
switch day
when "Mon" then go work
when "Tue" then go relax
when "Thu" then go iceFishing
when "Fri", "Sat"
if day is bingoDay
go bingo
go dancing
when "Sun" then go church
else go work
switch (day) {
case "Mon":
go(work);
break;
case "Tue":
go(relax);
break;
case "Thu":
go(iceFishing);
break;
case "Fri":
case "Sat":
if (day === bingoDay) {
go(bingo);
go(dancing);
}
break;
case "Sun":
go(church);
break;
default:
go(work);
}
Инструкции switch также могут использоваться без выражения управления, превращая их в более чистую альтернативу цепочкам if/else.
score = 76 grade = switch when score < 60 then 'F' when score < 70 then 'D' when score < 80 then 'C' when score < 90 then 'B' else 'A' # grade == 'C'
var grade, score;
score = 76;
grade = (function() {
switch (false) {
case !(score < 60):
return 'F';
case !(score < 70):
return 'D';
case !(score < 80):
return 'C';
case !(score < 90):
return 'B';
default:
return 'A';
}
})();
// grade == 'C'
Попытка/Перехват/Наконец
Выражения try имеют те же семантику, что и инструкции try в JavaScript, хотя в CoffeeScript вы можете опустить оба раздела catch и finally. Раздел catch также может опустить параметр ошибки, если он не нужен.
try allHellBreaksLoose() catsAndDogsLivingTogether() catch error print error finally cleanUp()
var error;
try {
allHellBreaksLoose();
catsAndDogsLivingTogether();
} catch (error1) {
error = error1;
print(error);
} finally {
cleanUp();
}
Цепочки сравнений
CoffeeScript заимствует цепочки сравнений из Python — что упрощает проверку, попадает ли значение в определённый диапазон.
cholesterol = 127 healthy = 200 > cholesterol > 60
var cholesterol, healthy; cholesterol = 127; healthy = (200 > cholesterol && cholesterol > 60);
Блочные регулярные выражения
Аналогично блочным строкам и комментариям, CoffeeScript поддерживает блочные регулярные выражения — расширенные регулярные выражения, игнорирующие внутренние пробелы и допускающие комментарии и интерполяцию. Моделируя модификатор /x из Perl, блочные регулярные выражения CoffeeScript ограничены /// и в значительной степени способствуют удобочитаемости сложных регулярных выражений. Согласно исходному коду CoffeeScript:
NUMBER = /// ^ 0b[01]+ | # binary ^ 0o[0-7]+ | # octal ^ 0x[\da-f]+ | # hex ^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal ///i
var NUMBER; NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; // binary // octal // hex // decimal
Размеченные шаблоны строк
CoffeeScript поддерживает размеченные шаблоны строк ES2015, которые позволяют настраивать интерполяцию строк. Если вы сразу же поставите перед строкой имя функции (без пробела между ними), CoffeeScript выведет эту комбинацию «функция плюс строка» в виде размеченного шаблона строки ES2015, который будет соответствующим образом работать: функция вызывается с параметрами, представляющими входящий текст и части выражения, составляющие интерполированную строку. Затем функция может собрать эти части в выходную строку, обеспечивая пользовательскую интерполяцию строк.
upperCaseExpr = (textParts, expressions...) ->
textParts.reduce (text, textPart, i) ->
text + expressions[i - 1].toUpperCase() + textPart
greet = (name, adjective) ->
upperCaseExpr"""
Hi #{name}. You look #{adjective}!
"""
var greet, upperCaseExpr;
upperCaseExpr = function(textParts, ...expressions) {
return textParts.reduce(function(text, textPart, i) {
return text + expressions[i - 1].toUpperCase() + textPart;
});
};
greet = function(name, adjective) {
return upperCaseExpr`Hi ${name}. You look ${adjective}!`;
};
Модули
Модули ES2015 поддерживаются в CoffeeScript, с очень похожим import и export синтаксисом:
import './local-file.js' # Must be the filename of the generated file
import 'package'
import _ from 'underscore'
import * as underscore from 'underscore'
import { now } from 'underscore'
import { now as currentTimestamp } from 'underscore'
import { first, last } from 'underscore'
import utilityBelt, { each } from 'underscore'
import dates from './calendar.json' assert { type: 'json' }
export default Math
export square = (x) -> x * x
export class Mathematics
least: (x, y) -> if x < y then x else y
export { sqrt }
export { sqrt as squareRoot }
export { Mathematics as default, sqrt as squareRoot }
export * from 'underscore'
export { max, min } from 'underscore'
export { version } from './package.json' assert { type: 'json' }
import './local-file.js';
import 'package';
import _ from 'underscore';
import * as underscore from 'underscore';
import {
now
} from 'underscore';
import {
now as currentTimestamp
} from 'underscore';
import {
first,
last
} from 'underscore';
import utilityBelt, {
each
} from 'underscore';
import dates from './calendar.json' assert {
type: 'json'
};
export default Math;
export var square = function(x) {
return x * x;
};
export var Mathematics = class Mathematics {
least(x, y) {
if (x < y) {
return x;
} else {
return y;
}
}
};
export {
sqrt
};
export {
sqrt as squareRoot
};
export {
Mathematics as default,
sqrt as squareRoot
};
export * from 'underscore';
export {
max,
min
} from 'underscore';
export {
version
} from './package.json' assert {
type: 'json'
};
Динамический импорт также поддерживается с обязательными скобками:
# Your browser must support dynamic import to run this example.
do ->
{ run } = await import('./browser-compiler-modern/coffeescript.js')
run '''
if 5 < new Date().getHours() < 9
alert 'Time to make the coffee!'
else
alert 'Time to get some work done.'
'''
// Your browser must support dynamic import to run this example.
(async function() {
var run;
({run} = (await import('./browser-compiler-modern/coffeescript.js')));
return run(`if 5 < new Date().getHours() < 9
alert 'Time to make the coffee!'
else
alert 'Time to get some work done.'`);
})();
Обратите внимание, что компилятор CoffeeScript не разрешает модули; запись import или export в CoffeeScript даст в результате import или export оператор в выходном коде. Такие операторы могут выполняться всеми современными браузерами (когда скрипт ссылается через <script type="module">) и Node.js, когда выходные .js файлы находятся в папке, где родительский package.json файл содержит "type": "module". Поскольку среда выполнения оценивает сгенерированный вывод, операторы import должны ссылаться на выходные файлы; поэтому, если file.coffee выводится как file.js, он должен быть указан как file.js в операторе import, включая расширение .js.
Кроме того, любой файл с import или export операторами будет выводиться без обертки безопасности функции верхнего уровня; другими словами, импорт или экспорт модулей автоматически приведет к незащищенному режиму работы для этого файла. Это связано с тем, что, согласно спецификации ES2015, import или export операторы должны находиться на самом верхнем уровне.
Встроенный JavaScript
Надеемся, вам никогда не придется его использовать, но если вам нужно вставить фрагменты JavaScript в ваш CoffeeScript, вы можете использовать обратные кавычки для их прямого пропуска.
hi = `function() {
return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;
hi = function() {
return [document.title, "Hello JavaScript"].join(": ");
};
Экранируйте обратные кавычки обратными слешами: \` становится `.
Экранируйте обратные слеши перед обратными кавычками с помощью дополнительных обратных слешей: \\\` становится \`.
markdown = `function () {
return \`In Markdown, write code like \\\`this\\\`\`;
}`
var markdown;
markdown = function () {
return `In Markdown, write code like \`this\``;
};
Вы также можете встраивать блоки JavaScript, используя тройные обратные кавычки. Это проще, чем экранировать обратные кавычки, если вам нужно их использовать внутри блока JavaScript.
```
function time() {
return `The time is ${new Date().toLocaleTimeString()}`;
}
```
function time() {
return `The time is ${new Date().toLocaleTimeString()}`;
}
;
JSX
JSX — это JavaScript, содержащий вставленные XML-элементы. Хотя он задуман для React, он не зависит от конкретной библиотеки или фреймворка.
CoffeeScript поддерживает вставленные XML-элементы без необходимости в отдельных плагинах или специальных настройках. XML-элементы будут скомпилированы как таковые, выведя JSX, который можно обработать как любой обычный JSX-файл, например, с помощью Babel с преобразованием React JSX. CoffeeScript не выводит React.createElement вызовы или любой код, специфичный для React или любого другого фреймворка. Вам нужно добавить еще один этап в свою цепочку сборки, чтобы преобразовать этот JSX в те вызовы функций, которые вы хотите, чтобы XML-элементы компилировались в.
Так же, как в JSX и HTML, обозначайте XML-теги, используя < и >. Вы можете интерполировать код CoffeeScript внутри тега, используя { и }. Чтобы избежать ошибок компиляции, при использовании < и > для обозначения «меньше» или «больше», вы должны заключать операторы в пробелы, чтобы отличить их от XML-тегов. Например, i < len, а не i<len. Компилятор старается быть снисходительным, когда уверен в вашем намерении, но всегда ставить пробелы вокруг операторов «меньше» и «больше» позволит избежать неоднозначности.
renderStarRating = ({ rating, maxStars }) ->
<aside title={"Rating: #{rating} of #{maxStars} stars"}>
{for wholeStar in [0...Math.floor(rating)]
<Star className="wholeStar" key={wholeStar} />}
{if rating % 1 isnt 0
<Star className="halfStar" />}
{for emptyStar in [Math.ceil(rating)...maxStars]
<Star className="emptyStar" key={emptyStar} />}
</aside>
var renderStarRating;
renderStarRating = function({rating, maxStars}) {
var emptyStar, wholeStar;
return <aside title={`Rating: ${rating} of ${maxStars} stars`}>
{(function() {
var i, ref, results;
results = [];
for (wholeStar = i = 0, ref = Math.floor(rating); (0 <= ref ? i < ref : i > ref); wholeStar = 0 <= ref ? ++i : --i) {
results.push(<Star className="wholeStar" key={wholeStar} />);
}
return results;
})()}
{rating % 1 !== 0 ? <Star className="halfStar" /> : void 0}
{(function() {
var i, ref, ref1, results;
results = [];
for (emptyStar = i = ref = Math.ceil(rating), ref1 = maxStars; (ref <= ref1 ? i < ref1 : i > ref1); emptyStar = ref <= ref1 ? ++i : --i) {
results.push(<Star className="emptyStar" key={emptyStar} />);
}
return results;
})()}
</aside>;
};
Старые плагины или форки CoffeeScript поддерживали синтаксис JSX и называли его CSX или CJSX. Они также часто использовали расширение файла .cjsx, но это больше не обязательно; подойдет обычное .coffee.
Аннотации типов
Статическую проверку типов можно осуществить в CoffeeScript, используя Flow’s синтаксис комментариев типов:
# @flow
###::
type Obj = {
num: number,
};
###
fn = (str ###: string ###, obj ###: Obj ###) ###: string ### ->
str + obj.num
// @flow
/*::
type Obj = {
num: number,
};
*/
var fn;
fn = function(str/*: string */, obj/*: Obj */)/*: string */ {
return str + obj.num;
};
CoffeeScript сам по себе не выполняет проверку типов; вышеприведенный JavaScript-код необходимо передать в Flow для проверки кода. Мы ожидаем, что большинство людей будут использовать инструмент сборки для этого, но вот как это сделать простейшим способом, используя CoffeeScript и Flow командные инструменты, предполагая, что вы уже установили Flow и последнюю версию CoffeeScript в вашей папке проекта:
coffee --bare --no-header --compile app.coffee && npm run flow
--bare и --no-header важны, так как Flow требует, чтобы первая строка файла была комментарием // @flow. Если вы настроили цепочку сборки на компиляцию CoffeeScript и передачу результата в Flow в оперативной памяти, вы сможете получить лучшую производительность, чем в этом примере; а правильный инструмент сборки должен уметь отслеживать ваши CoffeeScript-файлы, перекомпилировать и проверять их типы по мере сохранения.
Если вы знаете другой способ достижения статической проверки типов с помощью CoffeeScript, пожалуйста, создайте вопрос и сообщите нам.
Literate CoffeeScript
Помимо использования в качестве обычного языка программирования, CoffeeScript также можно писать в «literate» режиме. Если вы назовете свой файл с расширением .litcoffee, вы можете написать его как документ Markdown — документ, который также является исполняемым кодом CoffeeScript. Компилятор будет рассматривать все отступы блоков (способ Markdown обозначить исходный код) как исполняемый код, а всё остальное — как комментарии. Блоки кода также должны быть разделены от комментариев по крайней мере одной пустой строкой.
Просто для примера, немного компилятора реализовано в этом формате: Посмотрите как документ, сырой и правильно выделенный в текстовом редакторе.
Несколько замечаний:
- Блоки кода должны сохранять согласованный отступ относительно друг друга. Когда компилятор анализирует ваш Literate CoffeeScript-файл, он сначала отбрасывает все строки, не являющиеся блоками кода, а затем анализирует оставшиеся как обычный CoffeeScript-файл. Поэтому блоки кода должны быть написаны так, как если бы строки комментариев не существовали, с согласованным отступом (включая использование табуляции или пробелов).
- В тех же строках, блоки кода внутри пунктов списков или блоков цитат не рассматриваются как исполняемый код. Поскольку пункты списков и блоки цитат подразумевают свой отступ, было бы неоднозначно, как обрабатывать отступ между последовательными блоками кода, когда некоторые из них находятся внутри этих блоков, а другие нет.
- Элементы списка могут иметь не более одного абзаца. Второй абзац элемента списка был бы отступлен после пустой строки и, следовательно, неотличим от блока кода.
Карты исходных кодов
CoffeeScript включает поддержку генерации карт исходных кодов — способа указать вашей JavaScript-среде выполнения, какая часть вашего CoffeeScript-программы соответствует оцениваемому коду. Браузеры, которые её поддерживают, автоматически могут использовать карты исходных кодов для отображения исходного кода в отладчике. Чтобы сгенерировать карты исходных кодов вместе с JavaScript-файлами, передайте флаг --map или -m компилятору.
Для полного введения в карты исходных кодов, их работу и подключение в браузере прочитайте HTML5 Tutorial.
Cake и Cakefiles
CoffeeScript включает (очень) простую систему сборки, аналогичную Make и Rake. Естественно, она называется Cake и используется для задач, которые строят и тестируют сам язык CoffeeScript. Задачи определяются в файле с именем Cakefile, и их можно вызвать, выполнив cake [task] из каталога. Чтобы вывести список всех задач и опций, просто введите cake.
Определения задач написаны на CoffeeScript, поэтому вы можете поместить в свой Cakefile произвольный код. Определите задачу с именем, подробным описанием и функцией, которую нужно вызвать при выполнении задачи. Если ваша задача принимает параметр командной строки, вы можете определить параметр с короткими и длинными флагами, и он будет доступен в объекте options. Вот задача, которая использует API Node.js для перестройки парсера CoffeeScript:
fs = require 'fs'
option '-o', '--output [DIR]', 'directory for compiled code'
task 'build:parser', 'rebuild the Jison parser', (options) ->
require 'jison'
code = require('./lib/grammar').parser.generate()
dir = options.output or 'lib'
fs.writeFile "#{dir}/parser.js", code
var fs;
fs = require('fs');
option('-o', '--output [DIR]', 'directory for compiled code');
task('build:parser', 'rebuild the Jison parser', function(options) {
var code, dir;
require('jison');
code = require('./lib/grammar').parser.generate();
dir = options.output || 'lib';
return fs.writeFile(`${dir}/parser.js`, code);
});
Если вам нужно вызвать одну задачу перед другой — например, выполнить build перед test, вы можете использовать функцию invoke: invoke 'build'. Задачи Cake — это минимальный способ экспонировать ваши функции CoffeeScript в командной строке, поэтому не ожидайте никаких встроенных улучшений. Если вам нужны зависимости или асинхронные обратные вызовы, лучше поместить их в сам код, а не в задачу Cake.
"text/coffeescript" Теги скрипта
Хотя для серьёзного использования это не рекомендуется, CoffeeScripts можно включить непосредственно в браузер, используя теги <script type="text/coffeescript">. Исходный код включает сжатую и минимизированную версию компилятора (Скачать текущую версию здесь, 77к после сжатия) в виде docs/v2/browser-compiler-legacy/coffeescript.js. Включите этот файл на странице с inline тегами CoffeeScript, и он будет компилировать и вычислять их в порядке следования.
Обычные замечания по CoffeeScript применяются — ваши inline скрипты будут выполняться внутри обёртки closure, поэтому если вы хотите экспонировать глобальные переменные или функции, прикрепите их к объекту window.
Интеграции
CoffeeScript является частью огромной экосистемы JavaScript, и многие библиотеки помогают интегрировать CoffeeScript с JavaScript. Здесь перечислены основные проекты, особенно те, которые обновлены для работы с CoffeeScript 2; больше можно найти на страницах вики. Если вы считаете, что какой-то проект должен быть добавлен в этот раздел, пожалуйста, откройте вопрос или запросите исправление. Проекты перечислены в алфавитном порядке по категориям.
Инструменты сборки
Редакторы кода
Фреймворки
Проверяющие и форматировщики
Тестирование
Неподдерживаемые возможности ECMAScript
Есть несколько особенностей ECMAScript, которые CoffeeScript намеренно не поддерживает.
let и const: переменные со сферой блока и защитой от переприсваивания
Когда CoffeeScript разрабатывался, var было намеренно опущено. Это было сделано, чтобы избежать необходимости усложнять для разработчиков задачу управления переменными, сосредоточившись на объявлении переменной (var foo) вместо присваивания (foo = 1). Компилятор CoffeeScript автоматически заботится об объявлении, генерируя var инструкции в начале каждого блока функций. Это предотвращает случайное объявление глобальной переменной.
let и const добавляют полезную возможность в JavaScript, позволяя объявлять переменные в области блока, например, в теле инструкции if или в теле цикла for, тогда как var всегда объявляет переменные в области всей функции. Когда разрабатывался CoffeeScript 2, велись дискуссии о том, достаточно ли полезна эта функциональность, чтобы перевесить простоту отсутствия необходимости учитывать объявление переменных в CoffeeScript. В итоге было решено, что простота предпочтительнее. В CoffeeScript по-прежнему существует только один тип переменной.
Обратите внимание, что const защищает только от переприсваивания переменной; это не мешает изменять значение переменной, как обычно делают константы в других языках:
const obj = {foo: 'bar'};
obj.foo = 'baz'; // Allowed!
obj = {}; // Throws error
Именованные функции и объявления функций
Новички в CoffeeScript часто задаются вопросом, как сгенерировать JavaScript function foo() {}, в отличие от foo = function() {}, которые генерирует CoffeeScript. Первый вариант — это объявление функции, а второй — выражение функции. Как указано выше, в CoffeeScript всё является выражением, поэтому мы отдаём предпочтение выражению. Поддержка только одного варианта помогает избежать путаницы, которая может возникнуть из-за тонких различий между двумя формами.
Фактически, foo = function() {} создаёт анонимную функцию, которая присваивается переменной с именем foo. Некоторые очень ранние версии CoffeeScript именовали эту функцию, например foo = function foo() {}, но это было отброшено из-за проблем совместимости с Internet Explorer. В течение некоторого времени это доставляло неудобства, так как эти функции не имели имён в отслеживании стека; но современные среды выполнения JavaScript определяют имена таких анонимных функций по именам переменных, которым они присваиваются. Учитывая это, проще сохранить текущее поведение.
get и set сокращённая синтаксическая конструкция ключевых слов
get и set, как ключевые слова, предшествующие функциям или методам классов, намеренно не реализованы в CoffeeScript.
Это делается для избежания неоднозначности, так как в CoffeeScript такая конструкция выглядит идентично вызову функции (например, get(function foo() {})); и потому что существует альтернативный синтаксис, который немного более объёмный, но столь же эффективен:
screen =
width: 1200
ratio: 16/9
Object.defineProperty screen, 'height',
get: ->
this.width / this.ratio
set: (val) ->
this.width = val * this.ratio
var screen;
screen = {
width: 1200,
ratio: 16 / 9
};
Object.defineProperty(screen, 'height', {
get: function() {
return this.width / this.ratio;
},
set: function(val) {
return this.width = val * this.ratio;
}
});
Изменения, требующие переписывания кода, от CoffeeScript 1.x до 2
CoffeeScript 2 стремится выводить как можно больше идиоматического синтаксиса ES2015+ с минимальным количеством изменений, требующих переписывания, от CoffeeScript 1.x. К сожалению, некоторые изменения, требующие переписывания, были неизбежны.
Связанные (стрелочные) функции
В CoffeeScript 1.x, => компилировался в обычную function, но с ссылками на this/@ переписанными на использование внешней области this, или с внутренней функцией, привязанной к внешней области с помощью .bind (отсюда и название «связанная функция»). В CoffeeScript 2, => компилируется в ES2015 стрелочные функции, которые ведут себя немного иначе. Основное различие состоит в том, что в ES2015 стрелочные функции не имеют объекта =>.
outer = -> inner = => Array.from arguments inner() outer(1, 2) # Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2
var outer;
outer = function() {
var inner;
inner = () => {
return Array.from(arguments);
};
return inner();
};
outer(1, 2); // Returns '' in CoffeeScript 1.x, '1, 2' in CoffeeScript 2
Значения по умолчанию для параметров функций и элементов, деструктурированных в выражениях
Согласно спецификации ES2015 по параметрам функций по умолчанию и значениям по умолчанию для деструктурируемых элементов, значения по умолчанию применяются только когда значение отсутствует или undefined. В CoffeeScript 1.x значение по умолчанию применялось в этих случаях, но также и если значение null.
f = (a = 1) -> a f(null) # Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
var f;
f = function(a = 1) {
return a;
};
f(null); // Returns 1 in CoffeeScript 1.x, null in CoffeeScript 2
{a = 1} = {a: null}
a # Equals 1 in CoffeeScript 1.x, null in CoffeeScript 2
var a;
({a = 1} = {
a: null
});
a; // Equals 1 in CoffeeScript 1.x, null in CoffeeScript 2
Связанные генераторные функции
Связанные генераторные функции, а.к.а. стрелочные генераторные функции, не разрешены в ECMAScript. Вы можете написать function* или =>, но не то и другое. Поэтому, код CoffeeScript, подобный этому:
f = => yield this # Throws a compiler error
Нужно переписать обычным способом:
self = this f = -> yield self
var f, self;
self = this;
f = function*() {
return (yield self);
};
Классы компилируются в классы ES2015
Классы ES2015 и их методы имеют некоторые ограничения, выходящие за рамки ограничений обычных функций.
Конструкторы классов не могут вызываться без new:
(class)() # Throws a TypeError at runtime
Классы ES2015 не допускают связанных (стрелочных) методов. Компилятор CoffeeScript проходит некоторые манипуляции, чтобы сохранить поддержку для них, но одно, что невозможно учесть, это вызов связанного метода до его привязки:
class Base
constructor: ->
@onClick() # This works
clickHandler = @onClick
clickHandler() # This throws a runtime error
class Component extends Base
onClick: =>
console.log 'Clicked!', @
Методы классов не могут использоваться с new (редко используется):
class Namespace @Klass = -> new Namespace.Klass # Throws a TypeError at runtime
Из-за требуемой подгрузки для компиляции в классы ES2015, динамические ключи в методах классов не могут использовать значения из тела исполняемого класса, если методы не назначены в стиле прототипа.
class A
name = 'method'
"#{name}": -> # This method will be named 'undefined'
@::[name] = -> # This will work; assigns to `A.prototype.method`
super и this
В конструкторе производного класса (класса, extends другого класса), this нельзя использовать до вызова super:
class B extends A constructor: -> this # Throws a compiler error
Это также означает, что вы не можете передавать ссылку на this в качестве аргумента в super в конструкторе производного класса:
class B extends A
constructor: (@arg) ->
super @arg # Throws a compiler error
Это ограничение классов ES2015. В качестве обходного решения, присвойте значение this после вызова super:
class B extends A
constructor: (arg) ->
super arg
@arg = arg
var B;
B = class B extends A {
constructor(arg) {
super(arg);
this.arg = arg;
}
};
super и extends
Из-за конфликта синтаксиса с super с аксессорами, «голый» super (ключевое слово super без скобок) больше не компилируется в вызов super, передающий все аргументы.
class B extends A foo: -> super # Throws a compiler error
Аргументы могут быть явно переданы с использованием splats:
class B extends A foo: -> super arguments...
var B;
B = class B extends A {
foo() {
return super.foo(...arguments);
}
};
Или, если вам известно, что родительская функция не требует аргументов, просто вызовите super():
class B extends A foo: -> super()
var B;
B = class B extends A {
foo() {
return super.foo();
}
};
CoffeeScript 1.x позволял ключевому слову extends настроить прототипную наследование между функциями, и super можно было использовать для ручного назначения функций прототипа:
A = -> B = -> B extends A B.prototype.foo = -> super arguments... # Last two lines each throw compiler errors in CoffeeScript 2
Из-за перехода на ES2015 extends и super, использование этих ключевых слов для прототипных функций больше не поддерживается. Приведённый выше случай можно переписать следующим образом:
# Helper functions
hasProp = {}.hasOwnProperty
extend = (child, parent) ->
ctor = ->
@constructor = child
return
for key of parent
if hasProp.call(parent, key)
child[key] = parent[key]
ctor.prototype = parent.prototype
child.prototype = new ctor
child
A = ->
B = ->
extend B, A
B.prototype.foo = -> A::foo.apply this, arguments
// Helper functions
var A, B, extend, hasProp;
hasProp = {}.hasOwnProperty;
extend = function(child, parent) {
var ctor, key;
ctor = function() {
this.constructor = child;
};
for (key in parent) {
if (hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
return child;
};
A = function() {};
B = function() {};
extend(B, A);
B.prototype.foo = function() {
return A.prototype.foo.apply(this, arguments);
};
или
class A class B extends A foo: -> super arguments...
var A, B;
A = class A {};
B = class B extends A {
foo() {
return super.foo(...arguments);
}
};
JSX и операторы < и >
С добавлением JSX, символы < и > используются как операторы «меньше чем» и «больше чем», а также как разделители для тегов XML, таких как <div>. Для наилучших результатов, в общем случае, вы всегда должны заключать операторы в пробелы, чтобы отличить их от тегов XML: i < len, а не i<len. Компилятор пытается быть терпимым, когда он уверен, что вы имеете в виду, но всегда следует помещать пробелы вокруг операторов «меньше чем» и «больше чем», чтобы устранить неоднозначность.
Парсинг Literate CoffeeScript
Парсинг Literate CoffeeScript в CoffeeScript 2 был переработан, чтобы более тщательно не интерпретировать отступы списков как блоки кода; но это означает, что все блоки кода (если они не должны интерпретироваться как комментарии) должны быть разделены по меньшей мере одной пустой строкой от списков.
Блоки кода также должны теперь поддерживать постоянный уровень отступа — таким образом, отступ в один табулятор (или то, что вы считаете табуляцией, например, 2 пробела или 4 пробела) должен рассматриваться как «левая граница» вашего кода, при этом весь код в файле должен относиться к этой колонке.
Блоки кода, которые вы хотите сделать частью комментариев, а не выполнять, должны иметь по крайней мере одну строку (в идеале первую строку блока) полностью без отступа.
Обработка аргументов и строки shebang (#!)
В CoffeeScript 1.x, -- был необходим после пути и имени файла выполняемого скрипта, но перед любыми аргументами, переданными этому скрипту. Эта конвенция теперь устарела. Вместо:
coffee [options] path/to/script.coffee -- [args]
Теперь вы просто напишите:
coffee [options] path/to/script.coffee [args]
Устаревшая версия всё ещё будет работать, но перед выполнением скрипта будет выведено предупреждение.
В системах, отличных от Windows, файл .coffee может быть сделан исполняемым путём добавления строки shebang (#!) в начало файла и установки файла как исполняемого. Например:
#!/usr/bin/env coffee x = 2 + 2 console.log x
Если этот файл сохранён как executable.coffee, он может быть сделан исполняемым и запущен:
▶ chmod +x ./executable.coffee ▶ ./executable.coffee 4
В CoffeeScript 1.x это обычно приводило к ошибке при попытке передать аргументы в скрипт. Некоторые пользователи OS X обошли проблему, используя #!/usr/bin/env coffee -- в качестве первой строки файла. Однако это не работало на Linux, так как он не может разобрать строки shebang с более чем одним аргументом. Хотя такие скрипты всё ещё будут работать на OS X, CoffeeScript теперь выведет предупреждение перед компиляцией или оценкой файлов, начинающихся со слишком длинной строки shebang. Теперь, когда CoffeeScript 2 поддерживает передачу аргументов без необходимости --, мы рекомендуем просто изменить строки shebang в таких скриптах на #!/usr/bin/env coffee.
© 2009–2022 Jeremy Ashkenas
Licensed under the MIT License.
https://coffeescript.org/