Node.js v6.0 (Current) がリリースされました。
さて、とうとう皆さん待望の Node.js v6.0 がリリースされました!次のLTS候補です。LTSになるのは2016年の10月からの予定です。v6 の LTS 期間は明示化されてないですが、ルールに照らし合わせれば、LTSになってから 2年半がサポート期間なので、おそらく 2019年4月まではサポートされます。
Node v6.0.0 (Current) | Node.js
Node.js v6.0 の主な変更点
ES2015 support の改善
やっぱりこれが一番大きな変化ですね。
node.green を見てもらえればわかるかもしれませんが、 ES2015 のサポートがこれまでは 58% だったのが 96% まで大幅に拡大されました。
細かいところはnode.greenを見てもらうとして、いくつかのデフォルトで有効になった機能を紹介します。
デフォルトパラメータ
これは、関数に引数を渡す際に渡されない引数(undefinedな引数)にはデフォルトで特定の値にしてもらう、という機能です
function foo(a = 1, b = 2) { return a === 3 && b === 2; } foo(3) // true
// 引数の値をデフォルト引数の値として使うことも可能 function f(list, indexA = 0, indexB = list.length) { return [list, indexA, indexB]; } console.log(f([1,2,3]); // [1,2,3], 0, 3 console.log(f([1,2,3],1); // [1,2,3], 1, 3 console.log(f([1,2,3],1,2); // [1,2,3], 1, 2
デフォルトパラメータが書けるようになったので、いままでやっていたような もしも引数 a がundefinedだったらデフォルトで値をセットする
という処理が読みやすく、書きやすくなりました。
デストラクチャ
デストラクチャリング、和訳すると分配束縛と呼ばれる機能です。Clojureにある機能ですね。 これを利用すると配列やオブジェクトで設定した値を取り出しやすくなります。 一番良く使うのは値をswapさせる時かと思います。
具体的には以下のとおり。
var hoge = 123; var fuga =456; // 値をswapする var [fuga, hoge] = [hoge, fuga]; console.log(hoge); // 456 console.log(fuga); // 123 var [a, [b], [c], d] = ['hello', [', ', 'junk'], ['world']]; console.log(a + b + c); //hello, world (aに"hello", bに",", cに"world"が入ってる ) var pt = {x: 123, y: 444}; var {x, y} = pt; console.log(x, y); // 123 444
Rest パラメータ
可変長パラメータを持つ関数を作る時に arguments
を使わずに作れるようになりました。
// ...itemsでRestパラメータ function push(array, ...items) { // それをarrayにpush (ここはspread operator) array.push(...items); } var list = [1, 2, 3]; //list変数に数字をpush push(list, 4, 5, 6); console.log(list); //[1, 2, 3, 4, 5, 6]
正規表現に sticky option と unicode option が付く
sticky option は直前に実行した正規表現のlastindexを覚えておき、次に検索する時はその lastindex から検索するというオプションです。
var text = "First line\nsecond line"; var regex = /(\S+) line\n?/y; var match = regex.exec(text); console.log(match[1]); // "First" console.log(regex.lastIndex); // 11 var match2 = regex.exec(text); console.log(match2[1]); // "Second" console.log(regex.lastIndex); // 22
unicode option は正規表現のマッチ時にユニコードリテラルを使って書けるようになったり、サロゲートペアにマッチできるようになるためのフラグです。
"𠮷".match(/^.$/u)[0].length === 2
Proxy API
Proxyが提供してくれるのは、"ちゃんとした" メタプログラミングです。Proxyに関しては、Brendan Eich の構想を描いた発表資料があります。
www.slideshare.net
これが 2010 年という事で、Node.jsが流行りはじめたくらいに提案されたというのが歴史の深さを物語っていますが、Proxyは割りと色んな事ができるAPIです。5年経て、上のスライドで紹介されているものとは API が異なりますが、以下のことに使えます。
- Getter/Setter への インジェクションを行うことで、プロパティの変更や参照をされた時に処理を変えることができる。
- method_missing みたいな存在しないメソッドを呼び出した時に呼ばれるメソッドを定義できる。
などなど、名前の通り、代理オブジェクトとして呼び出されたときの処理を肩代わりすることができます。
Proxy API で Getter/Setter にインジェクトする。
こんな感じのことができます。
var handler = { get: function(target, name){ return name in target? target[name] : 37; } }; var p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37
存在しないプロパティを呼び出した時のデフォルトの振る舞いを定義できる。
const obj = {abc: 123}; const PropertyChecker = new Proxy(obj, { get(target, propKey, receiver) { if (!(propKey in target)) { throw new ReferenceError('Unknown property: '+propKey); } return Reflect.get(target, propKey, receiver); } }); console.log(PropertyChecker.abc); // 123 console.log(PropertyChecker.def); // Reference Error
これをうまく応用することで例えば method_missing 的な存在しないメソッドがあった時の振る舞いを定義できます。
Reflect API
一方で Reflect API は Proxy API と対になって使われることが多い API で、 Proxy が処理にインジェクトして、代理オブジェクトとして振る舞うのに対して、 Reflect API はそれのユーティリティオブジェクトで、対象となるオブジェクトに対して処理を反映(Reflect)させる動きをします。
最初にReflectを僕が知った時は対象となるオブジェクトが取れるならそれを直接変更すればいいので、いまいち使いドコロが分かってなかったんですが、ある程度使ってみると使い所が分かってきました。
使い所 その1 演算子じゃなくて、関数にしたい時
in
演算子を考えてみましょう
'assign' in Object // Object のプロパティに `assign` があるかどうか
これは演算子を使った式です、Reflect を使うと下記のように書くことができます。
Reflect.has(Object, 'assign'); // true
演算子じゃなくて関数で表現されているのがわかります。
他にも delete
演算子を Reflect を使って書き換えることが可能です。
// delete 演算子 var a = {abc: '123', def: '456'}; delete a['abc']; // {def: '456'}
// Reflectを使った場合 var a = {abc: '123', def: '456'}; Reflect.deleteProperty(a, 'abc'); // {def: '456'}
専用の演算子を使ったほうが短く書けますが、関数を使って代用できるようにしておくと読みやすさや反映する対象がわかりやすくなります。
使い所その2 例外よりも戻り値として扱いたい時
Object.defineProperty
というObjectに対してプロパティを定義する専用の関数がありますが、これは defineProperty
に失敗すると例外がthrowされます。そのため下記のように書かなくてはいけません。
try { Object.defineProperty(obj, name, desc); // 成功時の処理 } catch (e) { // 失敗時 }
Reflectができるようになると下記のように書けます
if (Reflect.defineProperty(obj, name, desc)) { // 成功時の処理 } else { // 失敗時の処理 }
--es_staging flag で有効になるもの
--es_staging
flag を付けるとES2015の機能がさらに増えます。ただし、 --es_staging
flag はまだ unstable の状態の機能を試すのに使うためのフラグなのであまり本番で積極的に使うものではありません。
末尾再帰呼び出し最適化
再帰呼び出しをした時に再起かどうかを内部的に検知して、再帰呼び出しを普通のループに変換します。 詳しくは下記の teppeis さんのブログが参考になります。
そこから持ってきた下記のスクリプトで試します。
'use strict'; // 今のところ strict mode でのみ有効 function factorial(n, acc) { if (n <= 1) return acc; return factorial(n - 1, n * acc); } [0,1,2,3,4,5,10,100,1000,10000,100000].forEach(function(n) {console.log(n, factorial(n, 1))});
階上計算処理ですが、これを普通に実行すると、 100000 の所で stack size オーバーフローでエラーになります。
$ node fact.js 0 1 1 1 2 2 3 6 4 24 5 120 10 3628800 100 9.332621544394418e+157 1000 Infinity 10000 Infinity /Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:2 function factorial(n, acc) { ^ RangeError: Maximum call stack size exceeded at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:2:19) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10) at factorial (/Users/yosuke/go/src/github.com/yosuke-furukawa/node/fact.js:4:10)
しかし、 --es_staging
で実行すると通るようになります。
$ node --es_staging fact.js 0 1 1 1 2 2 3 6 4 24 5 120 10 3628800 100 9.332621544394418e+157 1000 Infinity 10000 Infinity 100000 Infinity
Function#name
今までは無名関数だと .name
プロパティから関数名を取れませんでした、これが よくある無名関数を左辺に代入する書き方だと問題がありました。
// function.js var abc = () => {}; const def = function(){}; console.log(abc.name); // abc console.log(def.name); // def // ただし、この場合は 普通に名前付き関数の名前が採用される let ghi = funciton jkl() {}; console.log(ghi.name); //jkl
$ node function.js jkl
これを --es_staging
を付けると下記のようになります。
$ node --es_staging function.js abc def jkl
逆に有効になっていないもの
一番有名所としては import/export でしょうか。 これに関しては、ただいま絶賛議論が紛糾している所です。
- WRT PR #3 - CJS <-> ES import/export conversion · Issue #10 · nodejs/node-eps · GitHub
- WRT PR #3 - ES module path resolution · Issue #11 · nodejs/node-eps · GitHub
- WRT PR #3 - Module evaluation ordering · Issue #12 · nodejs/node-eps · GitHub
- WRT PR #3 - ES module detection · Issue #13 · nodejs/node-eps · GitHub
ES modules を Node に持ってくる、というのはかなり難しい問題で、今のところまだ有効な打開策が出ていません。そもそもまだ v8 でもパースできないのでパースできるようになったとして、どうやって解決するかという議論が始まったばかりです。
議論のまとめはこちら
module load 性能の向上
benchmark WG が継続してベンチマークを取るようにしてくれました。
benchmark working group では requireや起動時間といった性能を計測するマイクロベンチと acmeairと呼ばれるチケット予約管理アプリを使ったマクロベンチの2種類を実行しています。
v4.x と比較した時に require
した時の性能が初期ロードで 3倍高速に、二回目以降のロード(cache付きロード)で5倍高速になるようになりました。
ops/sec higher is better
また acmeair を使った全体のスループットに関しても若干の向上が見られます。
Buffer API の new Buffer() コンストラクタの廃止
セキュリティ上の理由から、 new Buffer() コンストラクタは廃止されました。 詳しくはこの辺りを見てもらうと良いかもしれません。
おまけ
Windows XP/Vista で node.js のサポートが終了しました。
まとめ
他にも色々と変更はありますが、全てを紹介しようとすると膨大な量なので一旦ここまでに停めておきます、すべての変更をちゃんと追いたい人は以下のChangelog を参考にしてください。
node/CHANGELOG.md at master · nodejs/node · GitHub
アップグレードするかどうか
まず v0.10 / v0.12 を使っている場合は今年の年末でサポートが切れてしまうので v4 もしくは v6 への upgradeを推奨します。
v4 を使っているユーザーは 2018年までサポートは続くのでまだアップグレードしなくても構いません、とはいえ上にあげたような新しい機能を使いたいユーザーはアップグレードを検討してください。
v5 を使っているユーザーはあと2ヶ月はサポートされますが、v5はLTS対象じゃないので、アップグレードを推奨します。v6 にアップグレードすることを検討してください。
Stable ラベルから Current ラベルへ
今までは Stable
というラベルが最新のバージョンにはついていましたが、 LTS と混同されやすいのでラベル名が Current
に変わりました。v6がLTSになったらその時はおそらく LTS
ラベルが付き、 v7 が Current
へと変わります。
v7 のリリースもまだ明示化されてないですが、今のところの計画では今から半年後の2016年の10月には次のv7.0がリリースされる予定です。
まとめ
Node.js v6.0 がリリースされました。
- 主な変更点
- ES2015 サポート拡充
- 性能向上
- new Buffer API の廃止
- アップグレード検討有無
- Stable から Current へ
という話をしました。