Марейн Хавербеке - Выразительный JavaScript
Когда передаётся строка, которая не выглядит как относительный или абсолютный путь, то предполагается, что это либо встроенный модуль, или модуль, установленный в директории node_modules. К примеру, require("fs") выдаст вам встроенный модуль для работы с файловой системой, а require("elife") попробует загрузить библиотеку из node_modules/elife/. Типичный метод установки библиотек – при помощи NPM, к которому я вернусь позже.
Для демонстрации давайте сделаем простой проект из двух файлов. Первый назовём main.js, и в нём будет определён скрипт, вызываемый из командной строки, предназначенный для искажения строк.
var garble = require("./garble");
// По индексу 2 содержится первый аргумент программы из командной строки
var argument = process.argv[2];
console.log(garble(argument));
Файл garble.js определяет библиотеку искажения строк, которая может использоваться как заданной ранее программой для командной строки, так и другими скриптами, которым нужен прямой доступ к функции garble.
module.exports = function(string) {
return string.split("").map(function(ch) {
return String.fromCharCode(ch.charCodeAt(0) + 5);
}).join("");
};
Замена module.exports вместо добавления к нему свойств позволяет нам экспортировать определённое значение из модуля. В данном случае, результатом запроса нашего модуля получится сама функция искажения.
Функция разбивает строку на символы, используя split с пустой строкой, и затем заменяет все символы на другие, с кодом на 5 единиц выше. Затем она соединяет результат обратно в строку.
Теперь мы можем вызвать наш инструмент:
$ node main.js JavaScript
Of{fXhwnuy
Установка через NPM
NPM, вскользь упомянутый в главе 10, это онлайн-хранилище модулей JavaScript, многие из которых написаны специально для Node. Когда вы ставите Node на компьютер, вы получаете программу npm, которая даёт удобный интерфейс к этому хранилищу.
К примеру, один из модулей NPM зовётся figlet, и он преобразует текст в “ASCII art”, рисунки, составленные из текстовых символов. Вот как его установить:
$ npm install figlet
npm GET https://registry.npmjs.org/figlet
npm 200 https://registry.npmjs.org/figlet
npm GET https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz
npm 200 https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz
[email protected] node_modules/figlet
$ node
> var figlet = require("figlet");
> figlet.text("Hello world!", function(error, data) {
if (error)
console.error(error);
else
console.log(data);
});
_ _ _ _ _ _ _
| | | | ___| | | ___ __ _____ _ __| | __| | |
| |_| |/ _ | |/ _ / / / _ | '__| |/ _` | |
| _ | __/ | | (_) | V V / (_) | | | | (_| |_|
|_| |_|___|_|_|___/ _/_/ ___/|_| |_|__,_(_)
После запуска npm install NPM создаст директорию node_modules. Внутри неё будет директория figlet, содержащий библиотеку. Когда мы запускаем node и вызываем require("figlet"), библиотека загружается и мы можем вызвать её метод text, чтобы вывести большие красивые буквы.
Что интересно, вместо простого возврата строки, в которой содержатся большие буквы, figlet.text принимает функцию для обратного вызова, которой он передаёт результат. Также он передаёт туда ещё один аргумент, error, который в случае ошибки будет содержать объект error, а в случае успеха – null.
Такой принцип работы принят в Node. Для создания букв figlet должен прочесть файл с диска, содержащий буквы. Чтение файла – асинхронная операция в Node, поэтому figlet.text не может вернуть результат немедленно. Асинхронность заразительна – любая функция, вызывающая асинхронную, сама становится асинхронной.
NPM – это больше, чем просто npm install. Он читает файлы package.json, содержащие информацию в формате JSON про программу или библиотеку, в частности, на каких библиотеках она основана. Выполнение npm install в директории, содержащей такой файл, автоматически приводит к установке всех зависимостей, и в свою очередь их зависимостей. Также инструмент npm используется для размещения библиотек в онлайновом хранилище NPM, чтобы другие люди могли их находить, скачивать и использовать.
Больше мы не будем углубляться в детали использования NPM. Обращайтесь на npmjs.org за документацией поиску библиотек.
Модуль file system
Один из самых востребованных встроенных модулей Node – модуль “fs”, что означает «файловая система». Модуль обеспечивает функционал для работы с файлами и директориями.
К примеру, есть функция readFile, читающая файл и делающая обратный вызов с содержимым файла.
var fs = require("fs");
fs.readFile("file.txt", "utf8", function(error, text) {
if (error)
throw error;
console.log("А в файле том было:", text);
});
Второй аргумент readFile задаёт кодировку символов, в которой нужно преобразовывать содержимое файла в строку. Текст можно преобразовать в двоичные данные разными способами, но самым новым из них является UTF-8. Если у вас нет оснований полагать, что в файле содержится текст в другой кодировке, можно смело передавать параметр "utf8". Если вы не задали кодировку, Node выдаст вам данные в двоичной кодировке в виде объекта Buffer, а не строки. Это массивоподобный объект, содержащий байты из файла.
var fs = require("fs");
fs.readFile("file.txt", function(error, buffer) {
if (error)
throw error;
console.log("В файле было ", buffer.length, " байт.",
"Первый байт:", buffer[0]);
});
Схожая функция, writeFile, используется для записи файла на диск.
var fs = require("fs");
fs.writeFile("graffiti.txt", "Здесь был Node ", function(err) {
if (err)
console.log("Ничего не вышло, и вот почему:", err);
else
console.log("Запись успешна. Все свободны.");
});
Здесь задавать кодировку не нужно, потому что writeFile полагает, что если ей на запись дали строку, а не объект Buffer, то её надо выводить в виде текста с кодировкой по умолчанию UTF-8.
Модуль “fs” содержит много полезного: функция readdir возвращает список файлов директории в виде массива строк, stat вернёт информацию о файле, rename переименовывает файл, unlink удаляет, и т. п. См. документацию на nodejs.org.
Многие функции “fs” имеют как синхронный, так и асинхронный вариант. К примеру, есть синхронный вариант функции readFile под названием readFileSync.
var fs = require("fs");
console.log(fs.readFileSync("file.txt", "utf8"));
Синхронные функции использовать проще и полезнее для простых скриптов, где дополнительная скорость асинхронного метода не важна. Но заметьте – на время выполнения синхронного действия ваша программа полностью останавливается. Если ей надо отвечать на ввод пользователя или другим программам по сети, затыки ожидания синхронного I/O приводят к раздражающим задержкам.
Модуль HTTP
Ещё один основной модуль — "http". Он даёт функционал для создания HTTP серверов и HTTP-запросов.
Вот всё, что нужно для запуска простейшего HTTP сервера:
var http = require("http");
var server = http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("<h1>Привет!</h1>
Вы запросили `"
request.url + "`
");response.end();
});
server.listen(8000);
Запустив скрипт на своей машины, вы можете направить браузер по адресу localhost:8000/hello, таким образом создав запрос к серверу. Он ответит небольшой HTML-страницей.
Функция, передаваемая как аргумент к createServer, вызывается при каждой попытке соединения с сервером. Переменные request и response – объекты, представляющие входные и выходные данные. Первый содержит информацию по запросу, например свойство url содержит URL запроса.
Чтобы отправить что-то назад, используются методы объекта response. Первый, writeHead, пишет заголовки ответа (см. главу 17). Вы даёте ему код статуса (в этом случае 200 для “OK”) и объект, содержащий значения заголовков. Здесь мы сообщаем клиенту, что он должен ждать документ HTML.
Затем идёт тело ответа (сам документ), отправляемое через response.write. Этот метод можно вызывать несколько раз, если хотите отправлять ответ по кускам, к примеру, передавая потоковые данные по мере их поступления. Наконец, response.end сигнализирует конец ответа.
Вызов server.listen заставляет сервер слушать запросы на порту 8000. Поэтому вам надо в браузере заходить на localhost:8000, а не просто на localhost (где портом по умолчанию будет 80).
Для остановки такого скрипта Node, который не завершается автоматически, потому что ожидает следующих событий (в данном случае, соединений), надо нажать Ctrl-C.