まえがき
第1章 Node.jsの全体像
第2章 npm(Node Package Manager)
第3章 サーバーとしてのNode.js
第4章 イベントループ
第5章 非同期プログラミング(コールバック)
第6章 コールバック地獄の解消
第7章 Stream
第8章 AltJSとTypeScriptを使った開発
第9章 コーディングガイドラインとESLint
第10章 Node.jsとセキュリティ
第11章 Node.jsのコアには、どのようなモジュールがあるのか
第12章 JavaScript & Node.js Tips集
付録A Node.jsの習得に役立つ情報を得るには
付録B ES2015(ES6)
付録C ES2016
付録D ES2017
付録E ES2018
付録F ES2019
付録G Node.js v13とv14について
あとがき
本書はNode.jsの採用をきっかけにして、同期プログラミングから非同期プログラミングの世界へと足を踏み入れた筆者の、学習をまとめた学習記録です。
The Node Way(Node流)を身に着けていく中で、筆者がスキルアップのために覚えた内容を振り返りながら一冊の本にまとめました。
・新しいJavaScriptでは何が変わったのか?
・コールバック地獄の解消
・非同期プログラミングにおける例外の捕捉
・クラスター化によるWEBサーバー運用
・・・をはじめ、Node.jsには学びのポイントがたくさんあります。
本書には主に筆者が、独学や実戦で学んだ内容が記載してあります。日記を読むような感覚で、気軽に楽んでいただけましたら幸いです。
・Node.jsについて理解を深め、ステップアップとして中級者を目指したい方
・非同期プログラミングや、シングルスレッドという言葉に馴染みがない方
・Node.jsそのものに対する技術的な興味がある方
本書はNode.js、およびその周辺環境に対する理解度を向上することを目的としています。Node.jsの入門書の次に読む、副読本のような形でお楽しみください。
なお入門書の次に読むことを想定しているため、環境構築の章を専用に設けておりません。そのため、入門書などで構築した環境をそのままお使いください。
筆者が主にNode.jsで開発していたのは数年前のことですが、今回はその知識をアップデートしながら本書を執筆いたしました。なお筆者は、Socket.IOのウェブソケットシステムでNode.jsを活用しております。その過程で得た、Node.jsそのものに対する知識を本書で執筆しました。
本書は技術書典7で頒布した「Node.js中級者を目指す」の加筆修正版です。印刷〆切の都合で書けなかった内容を追記し、また細かな修正を行ないました。
サンプルコードはGitHubにあります。
・https://github.com/konosumi/techbook-levelupnodejs-sample
本書におけるJavaScriptの動作確認は、執筆を開始した時点の最新版(Node.js v12.6.0)で行なっております。なお筆者は、macのnodebrewを利用いたしました。
# Homebrew で nodebrew をインストールします。
$ brew install nodebrew
# インストール可能な Node.js の一覧を表示します。
$ nodebrew ls-remote
# LTSであるv12のNode.js(2020/01/21時点)をインストールします。
# なお単なる nodebrew install v12.14.1 でも問題ありません(遅いですが)
$ nodebrew install-binary v12.14.1
# インストール後に使用を宣言すると有効になります
# これは nodebrew が Node.js のバージョン切り替え機能を有しているためです
$ nodebrew use v12.14.1
# nodebrewでインストールしたnodeのパスを通す
$ vim ~/.bash_profile
# 以下がなければ追加する
> # Node.js
> export PATH=$HOME/.nodebrew/current/bin:$PATH
$ source ~/.bash_profile
$ node --version
v12.14.1
個々の項目を学ぶ前に、まずはNode.jsの全体像から掴んでいきます。
さあ、学習の始まりです!
Node.jsを一言で表現するならば、サーバーサイドのJavaScript実行環境です。2009年から開発がスタートし、カリフォルニアで生まれ育ったライアン・ダールによって開発されました。
次の特徴があります。
1.非同期型
2.イベント駆動
Node はスケーラブルなネットワークアプリケーションを構築するために設計された、非同期型のイベント駆動のJavaScript環境です。
Node.jsの主な構成要素を次の図に示します。
この図は「Node.jsデザインパターン1」を参考に作成いたしました。「Node.jsデザインパターン」は中級者以上のウェブ開発者を対象とした書籍で、Node.jsを詳しく知る上でとても参考になる書籍です。
Node.jsではアプリケーションプログラムをJavaScriptで記述します。その実行を担っているのが、「V8 JavaScript Engine」です。
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.It is used in Chrome and in Node.js, among others.
It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linux systems that use x64, IA-32, ARM, or MIPS processors.
V8 can run standalone, or can be embedded into any C++ application.
V8はGoogleが開発しているオープンソースのJavaScript実行エンジンです。V8はNode.jsに限らず、Google Chromeでも採用されています。サーバーサイドでもブラウザーサイドでも活躍する、とても馴染みの深い存在です。
なおブラウザーにより、JavaScriptの実行エンジンは異なります。普段の開発で実行エンジンの違いを意識することは多くないかもしれませんが、フロントエンドエンジニアであれば知っておくことをオススメします。
・Firefox: SpiderMonkey
・Safari: JavaScriptCore
・Microsoft Edge: Chakra / V8
SpiderMonkeyは、C/C++で書かれたJavaScriptエンジンです。Firefoxを含む、Mozillaの複数の製品で使用されており、MPL2ライセンスの下で利用できます。
https://developer.mozilla.org/ja/docs/Mozilla/Projects/SpiderMonkey より
JavaScriptCore is the built-in JavaScript engine for WebKit. It currently implements ECMAScript as in ECMA-262 specification.
SafariはWebKitと呼ばれるWeb browser engineで動きますが、WebKitはJavaScriptCoreを含みます。またJavaScriptCoreはフレームワークとしても提供されており、Objective-CやSwiftを使ってiOSアプリでも動きます。
Microsoft EdgeはChakraと呼ばれるJavaScriptエンジンを採用していました。しかしChromium(Blink + V8)を採用することを決断し、Chromiumを搭載したEdgeが2020年に登場しました。
すでに正式リリースされていますが、日本では確定申告などへの影響を考慮して自動アップデートの提供は遅れています。
「Microsoft Edge」はこれまで独自のレンダリングエンジンとJavaScriptエンジン(「EdgeHtml」+「Chakra」)を採用してきましたが、「Google Chrome」などのベースとなっている「Chromium」に比べ、Web標準技術の実装や機能面で後塵を拝してきました。同社はバッテリー駆動時間の長さなどをアピールして普及に努めてきたものの、「Internet Explorer」時代のシェアを取り戻すことはできず、とうとう「Chromium」(「Blink」+「V8」)を採用することを決意したようです。
https://forest.watch.impress.co.jp/docs/news/1157163.html より
Node.jsに「10.x」や「8.x」といったバージョンがあるように、V8にもバージョンがあります。nodeが採用しているV8のバージョンは、Node.jsのバージョンによって決まります。
本書の執筆開始時点(Node.js 12.6.0)では、V8 7.5が使われていることがリリース一覧を見るとわかります。
V8のバージョンは重要です。なぜならNode.jsでES2015(ES6)をはじめとする新しいJavaScriptの文法が使えるのは、V8のバージョンアップにNode.jsが追従しているからです。
Node.jsはV8のモダンなバージョンに対して作られています。V8 を最新の状態に保つことでJavaScript ECMA-262の新機能を開発者にすみやかに提供し、継続的なパフォーマンスと安定性の向上を保証しています。
JavaScriptは毎年のように進化を続けています。この進化には名前がつけられており、ES2015/ES2016/ES2017・・・と呼ばれます。
なお場所によってES6やES7と呼ばれることもあります。6や7はEdition番号ですが、本来は年号つきの仕様書名が正式名称です。
・ECMAScript 1st Edition (ES) 1997年6月
・ECMAScript 2nd Edition (ES2) 1998年6月
・ECMAScript 3rd Edition (ES3) 1999年12月
・ECMAScript 4th Edition (ES4) (放棄)
・ECMAScript 5th Edition (ES5) 2009年12月
・ECMAScript 5.1th Edition (ES5.1) 2011年6月
・ECMAScript 2015 (ES2015/ES6) 2015年6月
・ECMAScript 2016 (ES2016/ES7) 2016年6月
・ECMAScript 2017 (ES2017/ES8) 2017年6月
・ECMAScript 2018 (ES2018/ES9) 2018年6月
・ECMAScript 2019 (ES2019/ES10) 2019年6月
ESとはECMAScriptの略称で、JavaScriptの標準規格のことです。国際的な標準化団体であるEcma Internationalにて、ECMA-262と呼ばれる規格番号で標準化されます。
ECMAScriptとEcma Internationalとは?
Ecma International(正式名:欧州電子計算機工業会; European Computer Manufacturers Association)は、コンピュータ・ハードウェアや通信、プログラム言語の標準規格を策定する非営利組織です。
ウェブの世界では、JavaScript言語の中核的規格であるECMA-262規格(別名:ECMAScript)を維持・管理する組織として有名です。
Ecma TC39とは、ECMAScriptを策定してる専門委員会のことです。TC39にはJavaScriptおよびブラウザーに関わる企業が、多く参加しています。
放棄されたES4では、Netscape Communications社やマイクロソフト社をはじめとする意見が対立し、最終的に議論が打ち切りとなってしまいました。
このような混乱があったため、ES3からES5への道のりは長い時間を要しています。
Node.jsで使える新しい文法は、MDN2のブラウザー実装状況で確認できます。
一覧でみたい場合は、有志によって運営されている対応表も参考になります。
・Node.js ECMAScript compatibility tables
・ECMAScript compatibility tables
─https://kangax.github.io/compat-table/es6/
Node.jsをバージョンアップすれば基本的にV8のバージョンも上がるため、より新しい文法が使いたければNode.jsのバージョンを最新に保つ必要があると覚えておきましょう。
Node.jsのプログラムでは、次のようなrequire()を多く見かけます。
1: const http = require('http');
require()はCommonJSという仕様を基本にしており、Node.jsに追加された記法です。またBrowserifyにも採用された実績があり、クライアントサイドでも活用例があります。
CommonJSとはJavaScriptを使い、サーバーサイドやコマンドラインツールをはじめとする、さまざまなアプリを開発するための標準的なAPIを定めることを目標としたプロジェクトです。
モジュールの歩み
Node.jsとCommonJSは2009年に開発され、大半のnpmパッケージでCommonJSが使われています。
Browserifyは2011年にリリースされた、すべての必要なオブジェクトをひとつのJavaScriptファイルにまとめるツールです。クライアントサイドのJavaScriptでnpmパッケージをrequireで読み込ませるために、CommonJSを採用しました。
https://www.webprofessional.jp/javascript-modules-bundling-transpiling/ より
require()はモジュールをインポートする側の記法ですが、モジュールをエクスポートする記法もあります。
1: module.exports = function hello() {
2: console.log('Hello world!');
3: };
4: module.exports = class Hello { /* ... */ }
モジュールのエクスポートにはECMAScriptを使った記法もあり、js-primer(JavaScript入門書)の執筆でも有名なazu(@azu_re)さんのGitHub Gistをみると、両者の感覚的な違いがよくわかります。
・ES6 modulesでexportしたい / CommonJS modulesでexportしたい
・https://gist.github.com/azu/09ee80c1a3127737000a
ただしECMAScriptのModules機能を標準のNode.jsで使うためには、いくつかの注意事項があります。主な注意点としてコマンドへの「--experimental-modules3」の追加と、基本的にJSファイルの拡張子を「.mjs」とします。
または最も近い親のpackage.jsonに「"type": "module"」を追加すれば、「.js」でも利用可能です。
ECMAScript Modules
The --experimental-modules flag can be used to enable support for ECMAScript modules (ES modules).
● Files ending in .mjs.
● Files ending in .js, or extensionless files, when the nearest parent package.json file contains a top-level field "type" with a value of "module".
● Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=module.
なお詳しい内容が知りたい方は、hiroppy(@about_hiroppy)さんの記事が参考になります。hiroppyさんはNode.jsとwebpackのコミッターです。
・Node.jsの新しいモジュール方式の実験的導入
・https://blog.hiroppy.me/entry/nodejs-experimental-modules
ECMAScript Modulesを使うためにはわざわざオプションを有効にする必要があるため、Node.jsではCommonJSを重点的に勉強する必要があるように感じられます。しかしながら状況は時代とともに変化しており、一概にそうとは言い切れません。
Forget CommonJS. It's dead. We are server side JavaScript.
https://github.com/nodejs/node-v0.x-archive/issues/5132#issuecomment-15432598 より
前の発言を読むと、Node.jsはCommonJSを離れようとしていることがわかります。
Node.jsには次の特徴がありますが、それを支えているのがlibuvです。
・ノンブロッキングI/O
・イベント駆動プログラミング
・非同期プログラミング
libuvはC言語で作成されたライブラリーで、低レイヤーのノンブロッキングI/Oをプラットフォーム(windows, mac, unix...)間の互換性を保ちつつ提供します。
またイベントループ用のイベントキューや、ノンブロッキングI/Oによる非同期I/Oなども提供します。Nodeのコア開発チームによって開発され、Node.jsの強みや特徴を下支えしている存在がlibuvです。
libuv is a multi-platform support library with a focus on asynchronous I/O.
It was primarily developed for use by Node.js, but it’s also used by Luvit, Julia, pyuv, and others.
Node.jsでは、かつてイベント駆動のI/Oライブラリーとしてlibevが採用されていました。しかしlibevは、Unix系OSには対応していたものの、Windowsに対応していないという欠点があります。
そのためnode-v0.9より、libevはlivubに置き換えられたという、歴史的経緯があります。
The node.js project began in 2009 as a JavaScript environment decoupled from the browser.Using Google’s V8 and Marc Lehmann’s libev, node.js combined a model of I/O – evented – with a language that was well suited to the style of programming;
due to the way it had been shaped by browsers. As node.js grew in popularity, it was important to make it work on Windows, but libev ran only on Unix. The Windows equivalent of kernel event notification mechanisms like kqueue or (e)poll is IOCP. libuv was an abstraction around libev or IOCP depending on the platform, providing users an API based on libev. In the node-v0.9.0 version of libuv libev was removed.