CoffeeScript — это язык, создающий абстракцию над JavaScript (как предполагает подобное имя). Это абстракция над синтаксисом JavaScript, а не над его концепциями: язык по-прежнему основан на функциях как объектах, которые могут связываться с другими объектами, и прототипах. наследование.
CoffeeScript поддерживает лучшие практики JavaScript, превращая абстракции, которые вы в любом случае написали бы или позаимствовали из фреймворка, в концепции языка для максимальной краткости. Он имеет этап компиляции — как и любой язык, который должен компилироваться в более низкий уровень, такой как C или Java.
Поскольку ковбойское кодирование не является моим предпочтительным способом работы, я подготовил пример разработки через тестирование с помощью jsTestDriver . В этой статье вы получите две вещи: введение в CoffeeScript и инструменты для его модульного тестирования (и, следовательно, как использовать TDD с CoffeeScript).
Инфраструктура
Базовая структура состоит из двух папок: src / и lib /; помните шаг компиляции. Мы поместим файлы .coffee в src / и скомпилируем их в эквиваленты .js в симметричном дереве в lib /.
Мы также добавляем файл jsTestDriver.conf, чтобы сообщить инфраструктуре модульного тестирования все загружаемые файлы, которые являются только «двоичными» сценариями .js:
server: http://localhost:4224 load: - lib/*.js
составление
Это первая версия теста, которую мне удалось написать, fizzbuzztest.coffee. Это тавтология, которая всегда должна проходить:
mytest = () -> assertEquals(1, 1)
tests = {
"test1is1": mytest
}
TestCase("tests for fizzbuzz kata", tests)
Здесь вы видите, что функции все еще являются объектами первого класса, но поддерживаются только анонимные функции. CoffeeScript — это Python / Ruby-подобный язык без точек с запятой, и с последним языком есть некоторые сходства и общие черты.
Я до сих пор использую старый синтаксис для вызова функций, хотя во многих случаях скобки могут быть опущены. CoffeeScript консервативен и даже принимает точки с запятой, если вы хотите написать их.
Я скомпилировал этот скрипт с кофе -o lib / -c src /. fizzbuzztest.js является результатом:
(function(){
var mytest, tests;
mytest = function() {
return assertEquals(1, 1);
};
tests = {
"test1is1": mytest
};
TestCase("tests for fizzbuzz kata", tests);
})();
По умолчанию глобальное пространство имен не затрагивается, и
ключевые слова var автоматически вводятся для его сохранения. Когда мне позже понадобилось глобальное пространство имен, я написал:
this.fizzbuzz = /* ... function definition ... */
это в данном случае является объект окна или другим глобальным объектом , где вы исполняете код.
Бег
Чтобы запустить тест, мы должны инициализировать тестовый драйвер (только один раз):
jsTestDriver java -jar JsTestDriver-1.3.2.jar --port 4224
jsTestDriver теперь будет слушать на локальном хосте: 4224. Загрузите этот URL в ваш браузер и перехватите его, нажав на ссылку. По запросу тесты будут выполняться внутри браузера: более подробную информацию см. В соответствующей статье .
Каждый раз, когда вы хотите запустить тесты, выполняйте из командной строки:
java -jar JsTestDriver-1.3.2.jar --tests all
Это полная история моего ката . Вот окончательная версия кода (предупреждение о спойлере!), С поддержкой добавления других факторов, кроме 3 или 5. Код, вероятно, уродливее среднего, но он компилируется:
tests = {
"test ordinary numbers are unchanged": ->
assertEquals(1, fizzbuzz(1))
assertEquals(2, fizzbuzz(2))
assertEquals(4, fizzbuzz(4))
assertEquals(142, fizzbuzz(142))
"test multiples of 3 become fizz": ->
assertEquals("Fizz", fizzbuzz(3))
assertEquals("Fizz", fizzbuzz(6))
assertEquals("Fizz", fizzbuzz(9))
"test multiples of 5 become buzz": ->
assertEquals("Buzz", fizzbuzz(5))
assertEquals("Buzz", fizzbuzz(10))
"test multiples of 3 and 5 become fizzbuzz": ->
assertEquals("FizzBuzz", fizzbuzz(15))
assertEquals("FizzBuzz", fizzbuzz(45))
}
TestCase("tests for fizzbuzz kata", tests)
newRule = (word, divisor) ->
(number) ->
return word if number % divisor == 0
""
newFizzBuzz = (rules) ->
(number) ->
result = ""
concatenation = (rule) ->
result = result + rule(number)
concatenation rule for rule in rules
return result if result
number
fizzRule = newRule("Fizz", 3)
buzzRule = newRule("Buzz", 5)
this.fizzbuzz = newFizzBuzz([fizzRule, buzzRule])
Комментарии к опыту
CoffeeScript предлагает более короткий синтаксис , который представляет собой некоторую кривую обучения, но не крутой . Я прошел весь пример за 1 час (однако я уже знал, как использовать jsTestDriver.)
Синтаксис формирует способ написания кода, упрощая некоторые вещи: я обнаружил, что использую функцию более высокого порядка, которая чаще создает другие , поскольку создание функции сейчас — это просто вопрос -> перед некоторыми строками кода.
Присвоение имен переменным также проще, поскольку вам нужно думать только об имени, а не о var или загрязнении области видимости. Больше времени уделять дизайну, а не языковым проблемам. Некоторые из них, такие как инструкция, если выражение удобны, но не обязательны, и есть благодаря вдохновению Ruby.
В CoffeeScript есть еще кое-что, например, опции для связывания функций, которые помогают не потерять ссылку на это. Однако вопрос заключается в том, имеет ли все это удобство большую ценность, чем время, затрачиваемое на изучение нового языка и добавление инфраструктуры для его работы — компилятор, хуки сборки и параллельное дерево, которое нужно игнорировать в вашей системе управления версиями.