Функциональный JavaScript с применением CoffeeScript и Node

Функциональный язык сценариев помогает справляться со сложностью Web-приложений

CoffeeScript славится сглаживанием шероховатостей JavaScript, но у него есть и другие преимущества, о которых стоит узнать. В этой статье Эндрю Гловер показывает, как четкий синтаксис CoffeeScript упрощает использование функциональных конструкций в библиотеках JavaScript, особенно при программировании на стороне сервера в Node.js. Статья завершается серией коротких демонстраций применения Underscore.js, вспомогательной библиотеки JavaScript, для обработки коллекций в CoffeeScript и Node.

Эндрю Гловер, президент компании, Stelligent Incorporated

Эндрю ГловерЭндрю Гловер является президентом компании Stelligent Incorporated , которая помогает другим фирмам решать проблемы качества программного обеспечения. Для этого используются эффективные стратегии тестирования и технологии непрерывной интеграции, которые позволяют коллективам разработчиков постоянно контролировать качество кода, начиная с ранних стадий разработки. Просмотрите блог Энди , там можно найти список его публикаций.



03.12.2012

CoffeeScript — это относительно новый язык, который составляет привлекательную альтернативу для разработчиков, уставших от недостатков JavaScript. CoffeeScript позволяет разработчикам программировать на легком, интуитивно понятном языке, который воспринимается как гибрид Ruby и Python. К тому же CoffeeScript компилирует код JavaScript для Web-приложений на основе браузера и гладко работает с Node.js при создании серверных приложений. Центральной темой этой статьи является третье преимущество использования CoffeeScript ― его работа с функциональной стороной JavaScript. Очищенный, модернизированный синтаксис CoffeeScript открывает мир функционального программирования, скрытый в библиотеках JavaScript.

Функциональное программирование ― в массы

Ни один из основных языков программирования (таких, как Java™, C++ или C#) не является по-настоящему функциональным языком, однако дополнительные библиотеки и конструкции в любом из них обеспечивают различные уровни функционального программирования. Еще важнее то, что такие языки, как Clojure, F# и Erlang, становятся популярными именно потому, что функциональное программирование обещает меньшее количество ошибок и повышенную производительность труда при разработке сложных приложений.

Как и JavaScript, функциональное программирование исключительно полезно и в то же время традиционно непопулярно. Если JavaScript считался игрушечным языком, то функциональное программирование приобрело репутацию чрезвычайно сложного. Но когда усилился спрос на высоко параллельные приложения, появилась потребность в альтернативе императивным стилям программирования с изменением состояния. Функциональное программирование оказалось не столь уж неоправданно сложным, элегантным инструментом систематизации сложностей, присущих приложениям определенного типа.

В этой статье мы рассмотрим функциональные сценарии CoffeeScript и Node с помощью библиотеки JavaScript, называемой Underscore. Сочетание этих трех технологий создает мощный набор инструментов для использования JavaScript в целях разработки программ для сервера или браузера с применением функционального программирования.

Обратите внимание, что эта статья продолжает мои предыдущие введения в JavaScript для Java-разработчиков и Node.js для Java-разработчиков. Я предполагаю, что в вашу среду разработки входит Node.js и что вы знакомы с основами программирования в Node.

Настройка CoffeeScript и Node

Если Node.js уже входит в вашу среду разработки, то для установки CoffeeScript можно использовать его менеджер пакетов (NPM). Следующая команда запускает NPM для глобальной установки пакета:

$> npm install -g coffee-script

Работа с CoffeeScript главным образом сводится к написанию программ, их сохранению в файлах .coffee и компиляции результатов в JavaScript. Синтаксис CoffeeScript близок к синтаксису JavaScript и должен быть знаком большинству разработчиков; например, в листинге 1 код CoffeeScript выглядит так же, как JavaScript, но без обычного нагромождения скобок и точек с запятой:

Листинг 1. Типичный код CoffeeScript
$> coffee -bpe "console.log 'hello coffee'"   
console.log('hello coffee');

Команда coffee служит ярлыком некоторых задач управления. Она компилирует файлы CoffeeScript в JavaScript, исполняет файлы CoffeeScript и даже выступает в качестве интерактивной среды или REPL (подобно irb в Ruby).

Вот так я помещаю свой сценарий в файл:

console.log "hello coffee"

Затем компилирую (или преобразую) этот файл в JavaScript:

$> coffee -c hello.coffee

Результатом становится файл hello.js. Поскольку результирующий JavaScript допустим для Node, я могу быстро запустить его в своей среде Node (листинг 2).

Листинг 2. JavaScript в среде Node
$> node hello.js 
hello coffee!

Иначе, можно использовать команду coffee для запуска оригинального файла .coffee, как показано в листинге 3.

Листинг 3. CoffeeScript в среде Node
$> coffee hello.coffee 
hello coffee!

Применение watchr

Сообщество сторонников открытого исходного кода создало ряд удобных утилит для работы с файлами (watchers), которые выполняют тесты, компилируют код и т.п. Обычно они работают через командную строку и очень легковесны. Мы настроим инструмент watcher для наблюдения за всеми файлами .coffee в нашей среде разработки и их компиляции в .js после сохранения.

Для этой цели я выбрал утилиту watchr, которая представляет собой библиотеку Ruby. Чтобы использовать watchr, нужны Ruby и RubyGems. Установив их в свою среду разработки, вы сможете выполнить следующую команду для установки watchr как глобальной библиотеки Ruby (включая соответствующие утилиты):

$> gem install watchr

Для определения файлов, за которыми нужно наблюдать, и производимых с ними действий в watchr используются регулярные выражения. Следующая команда настраивает watchr на компиляцию всех файлов .coffee из каталога src:

watch('src\/.*\.coffee') {|match| system "coffee --compile --output js/ src/"}

Обратите внимание, что команда coffee в данном случае помещает результирующие файлы .js в каталог js.

Это можно сделать в окне терминала, например, так:

$> watchr project.watchr

Теперь всякий раз, когда я вношу изменения в любой файл .coffee в своем каталоге src, watchr создает новый файл .js и помещает его в каталог js.


CoffeeScript на высоте 10000 футов

CoffeeScript предоставляет много полезных функций, которые значительно облегчают работу по сравнению с JavaScript. На верхнем уровне CoffeeScript исключает необходимость в фигурных скобках, точках с запятой, ключевых словах var и function. Одна из моих любимых особенностей CoffeeScript ― его определение функции, приведенное в листинге 4.

Листинг 4. Функции CoffeeScript ― это просто!
capitalize = (word) -> 
  word.charAt(0).toUpperCase() + word.slice 1

console.log capitalize "andy" //prints Andy

Здесь я объявляю простую функцию для написания слова CoffeeScript с прописной буквы. В CoffeeScript определению функции предшествует стрелка. Тело определения отделено отступом, поэтому в CoffeeScript не нужны фигурные скобки. Обратите также внимание на отсутствие круглых скобок. word.slice 1 из CoffeeScript просто компилируется в word.slice(1) для JavaScript. Тело функции отделено интервалом: весь код под строкой определения функции следует за пустой строкой. console.log без отступа внизу означает, что определение метода завершено. (Две эти особенности перенесены в CoffeeScript из Ruby и Python соответственно.)

Аналогичная функция JavaScript выглядела бы как в листинге 5.

Листинг 5. Даже в однострочном коде JavaScript много мусора
var capitalize = function(word) {
  return word.charAt(0).toUpperCase() + word.slice(1);
};

console.log(capitalize("andy"));

Переменные

CoffeeScript автоматически помещает ключевое слово var JavaScript перед любой определенной вами переменной. Так что при программировании на CoffeeScript не нужно помнить про var. (В JavaScript var ― необязательное ключевое слово. Но без него переменная становится глобальной, что почти всегда нехорошо.)

CoffeeScript также позволяет определить значения параметров по умолчанию, как показано в листинге 6.

Листинг 6. Значения параметров по умолчанию!
greeting = (recipient = "world") -> 
  "Hello #{recipient}"

console.log greeting "Andy" //prints Hello Andy
console.log greeting()      //prints Hello world

В листинге 7 показано, как значение параметра по умолчанию обрабатывается в соответствующем коде JavaScript.

Листинг 7. Замусоренный JavaScript
var greeting;

greeting = function(recipient) {
 if (recipient == null) recipient = "world";
 return "Hello " + recipient;
};

Условные операторы

CoffeeScript обрабатывает условные операторы с помощью таких ключевых слов, как and, or и not, как показано в листинге 8.

Листинг 8. Условные операторы CoffeeScript
capitalize = (word) -> 
	if word? and typeof(word) is 'string'
		word.charAt(0).toUpperCase() + word.slice 1
	else
		word

console.log capitalize "andy"   //выводит Andy
console.log capitalize null     //выводит null
console.log capitalize 2        //выводит 2
console.log capitalize "betty"  //выводит Betty

В листинге 8 для проверки существования объекта я использовал оператор ?. Прежде чем попытаться написать слово с прописной буквы, этот сценарий проверяет, что параметр word не равен null и что его тип ― string. Очень хорошо, что CoffeeScript позволяет использовать is вместо ==.

Определение класса для функционального программирования

JavaScript не поддерживает классы напрямую; это прототипно-ориентированный язык. Тех, кто воспитан на объектно-ориентированном программировании, это может показаться неудобным — нам нужны наши классы! CoffeeScript обеспечивает синтаксис class с целым рядом функций, которые после компиляции в стандартный JavaScript становятся функциями, определенными внутри функций.

В листинге 9 ключевое слово class используется для определения класса Message.

Листинг 9. CoffeeScript ― это классно!
class Message
	constructor: (@to, @from, @message) ->
	
	asJSON:  ->
		JSON.stringify({to: @to, from: @from, message: @message})

mess = new Message "Andy", "Joe", "Go to the party!"
console.log mess.asJSON()

В листинге 9 я использую ключевое слово constructor для определения конструктора. Затем я определяю метод (asJSON), указав имя после функции.


CoffeeScript и Node

Так как CoffeeScript компилируется в JavaScript, он естественным образом подходит для программирования в Node и может быть особенно полезным для дальнейшей рационализации и без того лаконичного кода Node. CoffeeScript особенно хорошо сглаживает многочисленные обратные вызовы Node, как видно из простого сравнения кода. В листинге 10 я определяю простое Web-приложение Node с использованием чистого JavaScript.

Листинг 10. Web-приложение Node.js на JavaScript
var express = require('express');

var app = express.createServer(express.logger());

app.put('/', function(req, res) {
  res.send(JSON.stringify({ status: "success" }));
});

var port = process.env.PORT || 3000;

app.listen(port, function() {
  console.log("Listening on " + port);
});

Переписав то же Web-приложение на CoffeeScript, мы выметаем синтаксический мусор из обратных вызовов Node, как показано в листинге 11.

Листинг 11. CoffeeScript упрощает Node.js
express = require 'express'

app = express.createServer express.logger()

app.put '/', (req, res) ->
  res.send JSON.stringify { status: "success" }

port = process.env.PORT or 3000

app.listen port, ->
  console.log "Listening on " + port

В листинге 11 я добавил оператор or, вместо оператора JavaScript ||. Я также обнаружил, что стрелку, указывающую на анонимную функции в app.listen, набрать легче, чем function().

CoffeeScript говорит со мной

Вероятно, вы уже поняли, что CoffeeScript предпочитает абстрактным символам разговорный английский. Вместо !== CoffeeScript позволяет использовать интуитивно понятное isnt; а === аналогичным образом превращается в is.

Если выполнить этот файл командой coffee -c, вы увидите, что CoffeeScript выдает почти точную копию кода JavaScript, приведенного в листинге 10. Идеальный JavaScript, выдаваемый CoffeeScript, работает с любой библиотекой JavaScript.


Функциональные коллекции и Underscore

Задуманная в качестве функционального пояса с инструментами для программирования на JavaScript, библиотека функций Underscore.js облегчает JavaScript-разработку. Среди прочего Underscore предлагает ряд ориентированных на коллекции функций, каждая из которых идеально подходит для решения конкретной задачи.

Например, предположим, что необходимо найти все нечетные числа в коллекции чисел, скажем, от 0 до 10, исключительно. Применение CoffeeScript вместе с Underscore позволит сэкономить время на наборе текста и вероятно, убережет от некоторых ошибок. В листинге 12 я привожу основной алгоритм, в то время как Underscore обеспечивает функцию агрегирования, в данном случае filter.

Листинг 12. Функция filter из библиотеки Underscore
_ = require 'underscore'

numbers = _.range(10)

odds = _(numbers).filter (x) -> 
	x % 2 isnt 0

console.log odds

Прежде всего, так как знак _ (то есть underscore - подчеркивание) является допустимым именем переменной, я сделал его ссылкой на библиотеку Underscore. Далее, я добавляю к функции filter, которая проверяет числа на нечетность, анонимную функцию. Обратите внимание, что я использовал ключевое слово CoffeeScript isnt, а не !== из JavaScript. Для указания того, что нужно отсортировать числа от 0 до 9, я использовал функцию range. Иначе, я мог бы подсчитать шаги для своего диапазона (считая парами) и начать с любого числа.

Функция filter возвращает массив, который представляет собой отфильтрованную версию входных данных, в данном случае, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]. Так что выполнение кода из листинга 12 дало бы [1, 3, 5, 7, 9].

Функция map ― еще одна из моих любимых функций для работы с коллекциями JavaScript, как показано в листинге 13.

Листинг 13. Функция map из библиотеки Underscore
oneUp = _(numbers).map (x) ->
	x + 1
	
console.log oneUp

Здесь результатом будет [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] Underscore просто добавляет единицу к каждому значению из моего диапазона numbers, так что не нужно перебирать значения вручную.

Если необходимо проверить те или иные аспекты коллекции, Underscore делает это с легкостью! Достаточно создать функцию, которая, например, выполняет проверку на четность, как показано в листинге 14.

Листинг 14. Функция even из библиотеки Underscore
even = (x) ->
	x % 2 is 0
	
console.log _(numbers).all(even)
console.log _(numbers).any(even)

Определив функцию even, я могу легко соединить ее с такими функциями Underscore, как all и any. В данном случае функция all применяет мою функцию even к каждому значению из диапазона numbers. Затем она возвращает логическое значение, указывающее, являются ли все числа четными (ложно). Аналогично, функция any возвращает логическое значение, указывающее, является ли какое-нибудь число четным (истинно).

Underscore может больше

Здесь я всего лишь касаюсь поверхности возможностей Underscore. В число других трюков входят связки функций, шаблоны JavaScript и глубокая проверка равенства. (См. раздел Ресурсы).

А что, если к коллекции значений не нужно применять никакие из этих функций, а требуется что-то еще? Без проблем! Для этого есть функция Underscore each. Она действует как простой итератор (то есть подспудно управляет логикой цикла, при каждой итерации выполняя указанные действия). Эта функция должна быть знакома тем, кто работал с Ruby или Groovy.

Листинг 15. Функция each из библиотеки Underscore
_.each numbers, (x) ->
	console.log(x)

В листинге 15 функция each принимает коллекцию (мой диапазон numbers) и функцию, которую следует выполнить с каждым значением из перебираемого массива. В данном случае я использую each для вывода значения текущей итерации на консоль. Так же легко можно было бы сделать что-нибудь вроде сохранения данных в базе данных, возврата результатов пользователю и т.п.


Заключение

CoffeeScript придает JavaScript-программированию свежесть и легкость, что делает его чрезвычайно интересным, особенно для тех, кто знаком с Ruby или Python. В этой статье я показал, как CoffeeScript берет что-то от каждого из этих языков, облегчая чтение кода в стиле JavaScript и значительно ускоряя работу. Сочетание CoffeeScript, Node и Underscore создает невероятно легкий и приятный набор инструментов разработки для основных сценариев функционального программирования. Со временем и практикой приобретенные здесь знания можно легко распространить на более сложные бизнес-приложения, которые опираются на динамическое взаимодействие Web- и мобильных систем.

Ресурсы

Научиться

Получить продукты и технологии

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Web-архитектура
ArticleID=848286
ArticleTitle=Функциональный JavaScript с применением CoffeeScript и Node
publish-date=12032012