30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

Node.jsの概要(1) モジュール機能

Node.js = サーバーサイドプログラミング?

最近Node.jsの勉強を始めたので、Node.jsの概要や全体像について、学んだことを記録していく。
どうもNode.jsは、自分が当初抱いていたイメージよりも、かなり広くて深い概念のような気がする。

当初、Node.jsは単にサーバーサイドプログラミングのための道具だと思っていた。JavaScriptでサーバーサイドプログラミングをするための道具。
だがどうやら、そういうものではないようだ。もっと広い概念に思えてきた。
JavaScriptそのものの機能を拡張するもの、そんな印象を受ける。
だから、サーバーサイドは別の言語で行い、クライアントサイドでのみJavaScriptを使うのだとしても、Node.jsは知っておいたほうがよさそうに思える。

ネット上の記事(主にQiita)を眺めているうちに多少は輪郭が見えてきたので、自分の知識を整理し、記録しておくための備忘録としてまとめておく。

JavaScriptに足らなかったもの

従来のJavaScriptに不足していた機能の一つに、モジュール機能がある。
プログラムのなかで、他のファイルを読み込む機能。JavaScriptではこの機能がサポートされていなかった。

他にもJavaScriptには欠点があり、それを解決するためのプロジェクトとして、CommonJSが始まった。

CommonJSについては説明するのが難しいらしいのだが、乱暴に言ってしまえば、JavaScriptの仕様のことらしい。

そしてこのCommonJSによって、ついにJavaScriptでもモジュール機能が使えるようになった。
つまりCommonJSは、JavaScriptをもっとイカした言語にしようとするプロジェクトであり、その一環として、モジュール機能を実現させたというわけだ。

だがCommonJSはあくまでも仕様に過ぎない。
その仕様に沿って実装されたのが、Node.jsである。そのため、Node.jsではモジュール機能が使える。

とはいえ現在のNode.jsは独自に拡張されており、CommonJSには準拠していないらしい。

モジュールとnpm

CommonJS、そしてNode.jsのおかげで、実際にJavaScriptでもモジュール機能が使えるようになった。
具体的には、exportsしたものを、require()で読み込めばいい。詳細は後述する。

自分が作ったモジュールはもちろん、最初からNode.jsに備え付けられているモジュールや、第三者が作成し配布しているモジュールも使える。

最初からNode.jsに備え付けられているモジュール

httpfsなどがあり、標準モジュールと呼ばれる。特別な手続きをすることなく、require()で読み込んで使える。

参照:Node.js API - 標準モジュール | プログラマーズ雑記帳

第三者が作成し配布しているモジュール

クライアントサイドJavaScriptにも、jQueryなどのライブラリが色々とあるが、それのNode.js版、という感じだと思う。

これを利用するためには、npmというツールを使う必要がある。
npmは最初からNode.jsに備え付けられており、これを使うことで、配布されているモジュールのインストールや管理が行えるようになる。
npmについてはあまりよく分かっていないので、別途調べる。

Browserify

Node.jsは、サーバーサイドのための言語であり、ブラウザからではなくターミナルから実行する。
しかし、せっかくモジュール機能という便利なものがJavaScriptで使えるようになったのだから、ブラウザ、つまりクライアントサイドJavaScriptでも、モジュール機能を使えるようにしたい。

そこで使うツールが、Browserifyである。

まず、Node.jsと同じように、何も意識せずにモジュール機能を使う。
だがそのままでは当然、利用できない。
だがBrowserifyを使えば、書いたコードをブラウザでも実行できる形に変換することが出来る。

つまり、Browserifyを使うことで、ブラウザでもモジュール機能を使えるようになるのだ。具体的な使い方は後述する。

C言語で記述したものを機械語コンパイルするイメージ。Browserifyが、コンパイラの役割を果たす。

実際に使ってみる

まずはモジュール機能をNode.jsで使ってみる。次に、それをブラウザでも使えるようにするため、Browserifyを導入し利用する。

モジュール機能

まず、モジュールとして取り込みたい機能を作り、それをexportsする。
今回はサンプルとして、受け取った文字列の最後に(笑)をつける関数を作成する。
この関数をlaughingと名付け、lol.jsというファイルに保存する。

// lol.jsの中身

var laughing = function(string){
    string = String(string);
    string += '(笑)';
    return string;
};
exports.laughing = laughing;

そしてそれを取り込む側のファイル(今回はmain.js)で、require()を使って読み込む。

// main.jsの中身

var lol = require('./lol');
var result = lol.laughing('test');
console.log(result);

そしてNode.jsでmain.jsを実行すると、test(笑)と表示され、モジュールがきちんと機能していることが分かる。

Browserify

だがこのままでは、Node.jsでは動いても、ブラウザでは動かない。
main.jsをブラウザで実行しようとすると、エラーになる。

<!DOCTYPE html>
<html lang="ja">
<head>
   <title>browserifyのテスト</title>
</head>
<body>
    <script type="text/javascript" src="main.js"></script>
</body>
</html>
main.js:1 Uncaught ReferenceError: require is not defined

なので、Browserifyを使ってブラウザでも解釈できる形にコードを変換していく。

まずはnpmを使ってBrowserifyをインストールする。

余談だが、このインストールで躓いた。
色々試したが、Browserifyがインストールできないというより、そもそもグローバルインストールが出来ないようだった。
npmによるモジュールのインストールは、個別のディレクトリにインストールしていく方法と、システム全体にインストールする方法であるグローバルインストールがあり、Browserifyは後者にあたる。
調べてみると、権限がないために、グローバルインストールの際に行われるディレクトリの作成に失敗しているようだった。
そのため、sudoでグローバルインストールを行ったら、成功した。
参照:npm installでError:EACCESがでたら - Qiita

インストールできたら、さっそく使ってみる。
require()を使っている側のファイル(今回はmain.js)をBrowserifyで変換し、それを新しいファイル(今回はbuild.js)として出力する。
以下のコマンドで出来る。

browserify main.js -o build.js

そうすると、次のようなbuild.jsが出来る。

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var laughing = function(string){
    string = String(string);
    string += '(笑)';
    return string;
};

exports.laughing = laughing;

},{}],2:[function(require,module,exports){
var lol = require('./lol');

var result = lol.laughing('test');

console.log(result);
},{"./lol":1}]},{},[2]);

これを、先ほど作ったhtmlで、main.jsの代わりにbuild.jsを読み込ませると、上手く機能していることが分かる。

<!DOCTYPE html>
<html lang="ja">
<head>
   <title>browserifyのテスト</title>
</head>
<body>
    <script type="text/javascript" src="build.js"></script>
</body>
</html>
build.js:15 test(笑)

このように、Browserifyを利用することで、クライアントサイドJavaScriptでもモジュール機能を使えるようになる。
そして、BrowserifyはNode.jsによって提供されている。Browserifyを利用するためには、Node.jsや、そのパッケージ管理ツールであるnpmが必要になるのだ。
そしてBrowserify以外にもクライアントサイドの開発を楽にしてくれるツールが色々とあるらしい。

だからこそ、サーバーサイドはJavaScript以外で書くつもりでありNode.jsを使う予定はないのだとしても、Node.jsをインストールしておいたほうがいい。このような便利な機能を使えるようになるのだから。

このような背景があるため、Node.jsに対して、単なるサーバーサイドの言語ではなく、JavaScriptそのものを拡張するものであるような印象を受けたのだ。

参考