Node.js v4.0.0 がリリースされました。

NodeConfEUに行ってたりして完全にブログにするのが送れましたが、 Node.js v4.0.0 がリリースされました。
f:id:yosuke_furukawa:20150918025348p:plain


https://nodejs.org/download/release/v4.0.0/

Node v4.0.0 (Stable) | Node.js


個人的には今年ずっとNode.js と io.js をまとめてきたり、時に中の人としてpatchを送ったりしてきたので本当に思い入れの深いリリースです。今までで一番リリースの思い入れが深いですね。

何が変わったのか

何回かこのブログで触れてますが、一番変わったのは開発体制と開発方法です。YAPCの発表でも触れましたが、BDFL (優しい終身の独裁者)モデルではなくなり、技術委員会によってけん引するモデルに変わりました。

体制が整った事で、今までよりかなりちゃんとPRはレビューされるようになりましたし、テストも不安定なテストが見直されたり、追加されたりすることで安定性が向上しました

今、Node.js には44名のアクティブなコラボレータが存在します。V8の中の動きに詳しい人もいれば、大津さんみたいなcrypto/tls周りのスペシャリストも居ます。彼らがちゃんとPRをレビューしてくれるのでpatchを送るだけでもかなり手厚い体制ができています。

そういう意味ではこの Node.js v4.0.0 は今までで一番安定したバージョンであると言えるのではないでしょうか。

もちろん安定しているとは言ってもバグは存在する可能性はあります。ただバグがあった場合も週次に次のバージョンがリリースされるため、これまでよりも修正されるスピードは速くなります。

機能的な差

おそらく Node.js v0.12 と v4.0 の差が気になるところだと思います。数えきれない程の差分があります。これをまとめる機会はまた別にあるので、そこで記述します。

どうしても気になる方は僕の記事のこれまでまとめたものをざっと眺めて貰えればと思います。

yosuke-furukawa.hatenablog.com

yosuke-furukawa.hatenablog.com

yosuke-furukawa.hatenablog.com


ここでは io.js v3.x から Node.js v4.0.0 までに入った機能について解説します。

ES6

Arrow 関数がデフォルトで利用可能になる

Arrow関数が追加されました。これまでは--harmony-arrow-functions と付ける必要がありましたが、これからは不要です。

'use strict';

// arrow funcs
setTimeout(()=>{
  console.log('foo');
}, 1000);

// Array examples 
const array = [1,2,3,4].map((i) => i*i);
array.forEach((i)=>{
  console.log(i); // 1, 4, 9, 16
});
Object.assign が使えるようになる

オブジェクト間のマージを行うためのメソッド、 Object.assign が使えるようになりました。
これまでは --harmony-object で有効でしたがこれからはフラグ無しで使えます。

js-next.hatenablog.com

var sym = Symbol()

var obj1 = {
 "str": 1,
}

var obj2 = {
 "str": 2,
 [sym]: 2 
}

var obj3 = Object.assign( obj1, obj2 )


console.log( obj1 == obj3 )      // true
console.log( obj1["str"] == 2 )  // true
console.log( obj1[ sym ] == 2 )  // true
// コンストラクタにまとめてアサイン
class Cat {
  constructor( name, age, color ) {
    Object.assign( this, { name, age, color } )
  }
}

var tama = new Cat( 'タマ', 9, '桃' )

console.log( tama.name == 'タマ' )  // true
SharedArrayBuffer が --harmony-sharedarraybuffer オプションで有効になる

SharedArrayBuffer は ArrayBuffer を worker 間で共有して扱うための新しいAPIです。

ただし、Node.jsには今のところ Worker を作るための仕組みが備わっていません。残念ながら Node.js の中では SharedArrayBuffer は単なる ArrayBuffer と変わりません。

なので今はまだNode.jsではテスト位でしか役に立ちませんが、一応Node.jsでもAPIを使うことはできます。

詳細: http://lars-t-hansen.github.io/ecmascript_sharedmem/shmem.html

基本的に WebWorker に代表される Worker は shared nothing、 つまりメモリを共有せず、値をコピーしてpostMessageなどでWorker間で協調しながら値を共有してきました。

このやり方はシンプルですが、コピーせずWorker 間でメモリを共有できたほうが効率的です(もちろん共有するな協調せよという考え方もあります)。

また SharedArrayBuffer を扱うためにはWorker間でのリソースの競合を防ぐ必要があります。これを可能にするために入っているのが Atomic API です。こちらは --harmony-atomics で有効になります。

SharedArrayBufferで作った配列に対して Atomic な操作を提供します。配列に対してadd/sub/and/or等の操作をする際に他のWorkerが操作していないか確認しロックを取る仕組みになっています。仕様によれば、futexの仕組みを使ってロックを取ると記述があります。

こんな感じで使います。

// SharedArrayBuffer and Atomics
// use flag --harmony-sharedarraybuffer --harmony-atomics

var sab = new SharedArrayBuffer(4); // SharedArrayBufferを作る
var uint8Array = new Uint8Array(sab); // Integer Shared Typed Array になる
for (i = 0; i < 4; i++) {
  uint8Array[i] = i;
}

uint8Array.forEach((x) => console.log(x)); // 0, 1, 2, 3

Atomics.add(uint8Array, 2, 10); // index 2 に 10 を足す
Atomics.sub(uint8Array, 3, 3); // index 3 から 3 を引く
Atomics.compareExchange(uint8Array, 0, 0, 1); // index 0 が 0 なら 1 にする
uint8Array.forEach((x) => console.log(x)); // 1, 1, 12, 0

console.log(Atomics.load(uint8Array, 2)); // 12 index 2 の値を get する
console.log(Atomics.store(uint8Array, 2, 19)); // 19 index 2 の値を19に書き換えて書き換えた値をgetする
uint8Array.forEach((x) => console.log(x)); // 1, 1, 19, 0

Intl オブジェクトがオプション付き有効になる。 (v3.1.0〜)

build オプションとして '--with-intl' が追加され、それを使ってビルドすると 国際化のためのBuilt-in ObjectであるIntl オブジェクトが有効になります。いわゆる ECMAScript 402 用のオブジェクトが使えるようになります。

$ ./configure --with-intl=full-icu --download=all
$ make
$ make install

Intl オブジェクトを使うと言語ロケールに依存した文字列比較、日付フォーマット、数値フォーマットを行うことが出来るようになります。

ECMAScript Internationalization API Specification – ECMA-402 Edition 1.0

developer.mozilla.org

// 文字列比較系

console.log(new Intl.Collator('ja', {
  sensitivity: 'base'  // 文字の派生系を同値と見なします、カタカナとひらがなの区別もつけません Ex. か == が , ぴ == ヒ, あ != い
}).compare('ハハ', 'パパ'));  // 0 つまり同値

console.log(new Intl.Collator('ja', {
  sensitivity: 'accent'  // 文字で発音が同じものを同値と見なします base の中で派生系は不等という指定です Ex. は == ハ , ぴ != ひ, あ != い
}).compare('ハハ', 'はは'));  // 0 つまり同値

console.log(new Intl.Collator('ja', { 
  sensitivity: 'accent', 
  ignorePunctuation: true // 句読点を無視するかどうか、デフォルトはfalse 
}).compare('わたしは、Lです', 'わたしはLです')); // 0 つまり同値

// 日付フォーマット系

var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
console.log(new Intl.DateTimeFormat('en').format(date)); // 12/20/2012
console.log(new Intl.DateTimeFormat('ja').format(date)); // 2012/12/20
console.log(new Intl.DateTimeFormat('ja-JP-u-ca-japanese').format(date)); // 平成24/12/20

// 数値フォーマット系
console.log(new Intl.NumberFormat('en').format(10000000)); // 10,000,000
console.log(new Intl.NumberFormat('ja', {style: 'currency', currency: 'JPY'}).format(10000000)); //¥10,000,000
console.log(new Intl.NumberFormat('en', {style: 'percent', }).format(0.230232)); // 23.023%

--tls-cipher-list というデフォルトの暗号化方式を変更する事ができるオプションが付く (v3.2.0〜)

暗号化方式をデフォルトの一覧から変更することができるオプションが付くようになりました。
例えばこれで使わない暗号強度の弱いものをデフォルトから省くといった事が可能になります。

これ自身は Node.js v0.12 には入っていたものの、 io.js には取り込まれていなかったもので、追加されることになりました。

$ node --tls-cipher-list="ECDHE-RSA-AES128-GCM-SHA256:!RC4"

こうすることで自分でデフォルトの暗号化リストを制限することができます。

--link-module という configure オプションが追加される (v3.3.0〜)

configure のオプションとしてNode.js内部にユーザーモジュールをbuilt-inすることができる、 --link-module オプションが付くようになりました。

背景として、 io.js v3.0や Node.js v4.0 は private モジュールを極力呼べないように外からは隠ぺいするようになりました(言い方を変えると今までは呼べました)。privateモジュールはリファクタリングされる事が多いので、semver上それらが使われてしまう事は好ましくありません。モジュールの安定性を求め、隠蔽されたので、privateモジュールは呼べなくなってしまいました。

そのため、内部モジュールを使って無理矢理ライブラリを作成するような事はできませんが、その代わりビルドオプションの--link-moduleでコアの一部に無理矢理自分のモジュールを入れることはできます。そうすると、コア内に自分のモジュールが組み込まれることになるため、内部メソッドを使う事も可能になります。ただし、通常のユーザーに取ってみればこれを必要とするケースは極わずかだと思います。コアの拡張を一旦試すようなNode.js コントリビュータもしくは Node.js の fork を作ってコアを拡張するような人達、はたまたどうしても内部のメソッドを呼びたい人達が使うオプションです

# cat test.js 
# console.log('hello world');
$ ./configure --link-module test.js
$ make
$ make install
$ node -e "require('test')" // hello world

Node.js v4.0.0 の性能面

もう一つ性能的な面でも改善されています。Node.js v0.12 と io.js v3.3 、 Node.js v4.0 のそれぞれでベンチマーク比較をしてくれているサイトがあるので紹介します。

https://raygun.io/blog/wp-content/uploads/2015/09/Screenshot-2015-09-09-16.01.51.png

raygun.io


このグラフは Benchmark 厨の Raygun というサイトのものです。 Express のアプリを作ってそれぞれどれくらい性能が上がったかを示したものです。このサイトによれば、 Node.js v0.12 と比較して 8% ほど改善しています。

f:id:yosuke_furukawa:20150918025958p:plain

もう一つ、 Daniel Khan という僕と同じくエヴァンジェリストの一人が計測したデータです、こちらでも高速化されてることがわかります。

apmblog.dynatrace.com


f:id:yosuke_furukawa:20150918030135p:plain

さらに高速化されてるだけじゃなくてメモリ消費量も下がっています。
今回から http_parser がバージョンアップし、性能改善されたため、高速化されたこと、またv8の改善やBuffer APIリファクタリングされたことの影響を受けてメモリ使用量にも影響を受けるようになりました。

Node.js v4.0.0 まとめ

基本的に大きな機能変更は v3.0.0 の時と変わりません。 v3.0.0 + Node.js v0.12 の差分 が v4.0.0 です。

機能的な面をおさらいすると

  • ES6強化 Arrow 関数 / Object.assign / SharedArrayBuffer
  • Intl オブジェクトが使えるようになる
  • 自分のモジュールを内部のコアに組み込めるようになる

性能的には

  • http_parser 改善により、 express などのライブラリを通して 8% 以上の改善
  • v8 のバージョンアップ、Buffer リファクタによるメモリ改善

などが見られます。この他にもたくさん機能はありますが、一旦ここまで。