技術探し

技術探し

JavaScriptを中心に記事を書いていきます :;(∩´﹏`∩);:

Node.jsのドキュメント管理について

じいちゃんが米寿なため、実家で書いています。
あと家族増えてました👨‍👨‍👦‍👦
左の4ヶ月の子。

先日、こちらのOSSドキュメント勉強会で話しました。

kbkz.connpass.com

今回はスライドの要約です。

how to manage the document of Node.js

ドキュメントの重要性

f:id:about_hiroppy:20171120021722p:plain

opensource.guide

ドキュメントは大変重要で、ドキュメントが豊富かどうがでそのフレームワーク・ライブラリが使用されるかどうかも左右されると私は思います。

また、ドキュメントの貢献はOSSへの最初のワンステップとしてはちょうどよく貴重なコミットです。

例えばフレームワークであれば、APIの仕様以外にもexamplesを含めget-startedがあればユーザに優しいと思います。

Node.js Foundationについて

Node.jsはNode.js Foundationというところで管理されています。
Node.js github.com

ここでは、約600人の人々が約120チームに分かれて活動をしています。
また、チームとしては34ヶ国語のサポートがされています。
基本的にissueは英語以外でも問題ないという認識です。

Node.js

github.com

  • 19,914commits
  • 457 releases
  • 1790 contributors
  • 2,527 watchers
  • 42,472 stars
  • 8,831 forks

Node.の中には様々なドキュメントがあります。
以下は一例です。

  • BUILDING.md
  • CHANGELOG.md
  • CODE_OF_CONDUCT.md
  • COLLABORATOR_GUIDE.md
  • CONTRIBUTING.md
  • CPP_STYLE_GUIDE.md
  • GOVERNANCE.md
  • API Documentation

今回はAPIドキュメントについて話しました。

APIドキュメント

Docs | Node.js

nodejs.orgにあるのですが、APIドキュメントだけはNodeの.jsコアにあります。
なのでサイトの管理はnodejs/websiteチームですが、APIドキュメントはnodejs/collaboratorチームが行います。

コミット数

$ git log --grep doc: --pretty=oneline --check | wc
    3367   20994  237596

Node.jsではコミットのルールがあり、エコシステムが必ず先頭につきます。
ドキュメントの場合は、doc: です。

コミット数は3367でした。ちなみにテストは2853でおそらくエコシステムの中で一番多いコミット数だと思います。

issue/prのコメント数によるラベルの種類

f:id:about_hiroppy:20171121085509p:plain

One Week in the Life of Node.js

レビュワー

  • Collaborators
    • PRを承認する権利を持つので必須
  • Documentation
  • Website
    • 主にスタイル
  • その他モジュールに特化したWG(e.g. promise, stream, V8, etc...)

レビューをするポイント

  • typo
  • プラットフォームの依存
    • e.g. これはwindowsでは動かない等
  • コーナーケース
    • e.g. この場合は動かない等
  • 英語修正
    • もちろん、完璧な英語じゃなくても問題ないと思ってます

スタイルガイド

多いので言いたかったのだけ抜粋。

  • ファイル名はlowercase-with-dashes.md
    • jsのファイル名やモジュール名は-が多い(特に昔は)
  • I, we, you, his, guysなどは避ける
    • they, folks, people, developersなどを使う

github.com

Pronouns  |  Google Developer Documentation Style Guide  |  Google Developers

書き方

## module
<!-- YAML
added: v0.10.0
-->

> Stability: 3 - Stable

description and examples.

### module.property
<!-- YAML
added: v0.10.0
-->

* Type

description of the property.

### module.someFunction(x, y, [z=100])
<!-- YAML
added: v0.10.0
-->

* `x` {String} the description of the string
* `y` {Boolean} Should I stay or should I go?
* `z` {Number} How many zebras to bring.

A description of the function.

github.com

yamlの部分はバージョンの管理でそのモジュールが足されたとかの履歴を表示します。

markedjs-yamlを使いHTMLに変換します。
lib/<module>.jsdoc/api/<module>.mdが必ず1:1の対応になります。

オートメーション

CI

Node.js [Jenkins]

CIはNode.js Foundationから提供されているJenkinsを使います。
ここでは様々なプラットフォーム上でテストが行われます。
ドキュメントでは主にスモークテストとlintのテストが実行されます。

Lint

ESLintを使っています。

eslint.org

コアコードと同じルールを適応することにより、コードの書き方の統一化を行っています。

# Makefile

LINT_JS_TARGETS = benchmark doc lib test tools
LINT_JS_CMD = tools/eslint/bin/eslint.js --cache \
    --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md \
    $(LINT_JS_TARGETS)

またdoc内にもeslintrc.ymlがあり、そこで適応できない部分がある場合は上書きしています。

ドキュメントをビルドする

HTMLへ変換します。

$ ./configure && make doc

./configureでNode.jsを生成して、それを使いdocを生成します。
そうすると以下のようなファイルが生成されます。

f:id:about_hiroppy:20171121091349p:plain

サポートツール

Node.jsではまだ使われてませんが、便利なツールを紹介しました。

APIの翻訳

ターゲットとの差分(行が増えた, 消えた等)が見やすく、複数人で翻訳がしやすいサービスです。 GoogleのプロダクトやVueなどで使われています。

gitlocalize.com

英語のタイポ検知

以前、PRでこれを使って修正したというのが来てこのツールを知りました。
修正もしてくれます。

github.com

まとめ

  • ドキュメントはめっちゃめっちゃ大切
  • 英語の主語には注意する
  • 自動化しよう

Code and Learn

来週のNode学園祭の2日目では、Code and Learnと呼ばれるNode.jsへコントリビュートする企画があります。
実際に世界中のNode.jsのコアメンバーの人たちがサポートをし、Node.jsへコミットします。大変おもしろい企画ですので是非参加してみてください:)

そのときに役に立てばいいなと思います。

nodefest.jp

Node.jsのパフォーマンスチューニングのtips

Node9が10/31に出ました🎉🎉🎉

Node v9.0.0 (Current) | Node.js

今回はNode単体の話なので、Express、Nginx等のチューニングに関してはココには書きません。
また、libuv等のコード内部の話もしません。

--inspect, --inspect-brk

もともとあった、--debugから移行されました。(v8.0.0 ~)
Chromeを使いデバッグ、プロファイリング等を使えるようになります。
ブラウザで使えるので、いつも使っている感じと同じです。
--inspect-brk--debug-brkと同様に最初の行にブレークポイントを設置し、起動します。

$ node --inspect test.js
Debugger listening on ws://127.0.0.1:9229/b565921e-23f2-4cee-b124-33e97fc3aa32
For help see https://nodejs.org/en/docs/inspector

chromeからchrome://inspect/#devicesを指定すると以下のように選択肢がでるので、そこからinspectを選ぶと起動します。
f:id:about_hiroppy:20171105204518p:plain

インスペクターのクライアント一覧: Debugging - Getting Started | Node.js
Inspector Help | Node.js
個人的には、NiMを入れると楽かなーと思います。
abouthiroppy.github.io

--trace-opt, --trace-deopt

コードの最適化の解析を行います。

$ node --trace-opt test.js
[marking 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> for optimized recompilation, reason: hot and stable, ICs with typeinfo: 46/67 (68%), generic ICs: 0/67 (0%)]
[compiling method 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> using TurboFan]
[optimizing 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> - took 1.867, 1.776, 0.019 ms]
[completed optimizing 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)>]
[marking 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> for optimized recompilation, reason: hot and stable, ICs with typeinfo: 23/23 (100%), generic ICs: 0/23 (0%)]
[compiling method 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> using TurboFan]
[optimizing 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> - took 0.659, 3.009, 0.049 ms]
[completed optimizing 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)>]

markingは再コンパイル用のマーキングで、それは再コンパイルされ最適化されます。
最適化が不可能な場合は、マーキングの代わりにdisabled optimizationというのが付きます。

上記をみればわかるように、その関数が最適化されたかどうかがわかります。

--prof

CPUプロファイリングです。 V8内のプロファイラの実行をサンプリングします。

$ node --prof test.js
$ ls
isolate-0x103000000-v8.log test.js
$ node --prof-process isolate-0x103000000-v8.log # logは読みづらいので読めるようにする

各セクションごとに情報が分かれます。

 [Summary]:
   ticks  total  nonlib   name
      3    5.0%    5.0%  JavaScript
     50   83.3%   83.3%  C++
      1    1.7%    1.7%  GC
      0    0.0%          Shared libraries
      7   11.7%          Unaccounted

取得されたサンプルの比率(5.0%, 83.3%, etc...)が割合となり、その言語のコードで発生したことを示します。
そして、各セクションを見るといいと思います。

セクション例

   ticks parent  name
   6326   44.2%  /lib/x86_64-linux-gnu/libm-2.15.so
   6325  100.0%    LazyCompile: *exp native math.js:91
   6314   99.8%      LazyCompile: *calculateMandelbrot http://localhost:8080/Demo.js:215

各セクションは、ツリーになっており、この場合は親コールの合計時間における44.2%がシステム内のmath.exp()を実行するのに使われています。
関数名の前の*はその時間が最適化された関数で費やされていることを示し、~の場合は最適化された関数ではないことを示します。

詳しくは公式が出している以下の記事を見ると、手順がわかりやすいと思います。
Easy profiling for Node.js Applications | Node.js

github.com

--trace-events-enabled

トレース情報を管理します。
--trace-events-enabledフラグを渡すと有効化されます。
カテゴリを指定したい場合は、--trace-event-categoriesを使い続けてカテゴリを指定します。
カテゴリデフォルトはnodev8になります。
chromechrome://tracing/を指定することにより、生成したのをロードすることが可能です。

$ node --trace-events-enabled test.js
$ node --trace-events-enabled --trace-event-categories v8,custom-category test.js

f:id:about_hiroppy:20171105200300p:plain

Tracing | Node.js v9.0.0 Documentation github.com www.chromium.org

--trace-gc

Garbage Collectionのトレースです。
メモリリークデバッグに役立つでしょう。

$ node --trace-gc test.js
[43929:0x102801c00]       39 ms: Scavenge 3.4 (6.3) -> 3.1 (7.3) MB, 0.9 / 0.0 ms  allocation failure
[43929:0x102801c00]       50 ms: Scavenge 3.6 (7.3) -> 3.5 (8.3) MB, 1.2 / 0.0 ms  allocation failure

$ node --optimize_for_size --max_old_space_size=4096 --gc_interval=100 #このようにV8のGCを操作することも可能

--expose-gcを指定することにより、手動でGCを起こすことも可能です。

メモリリーク周りは以下の記事を参考にするとわかりやすくていいと思います。

postd.cc

実例

github.com

node-report

公式が出しているモジュールです。
現在、node-reportはCoreとは別で切り分けられておりスタンドアローンですが、将来的にはCoreに入る予定です。
ネイティブのスタックトレース、ヒープ統計情報、プラットフォーム情報、リソース使用状況などが人間が読める形でレポート化されます。

$ npm i node-report
$ node -r node-report test.js
$ cat node-report.20171105.202142.9066.001.txt
================================================================================
==== Node Report ===============================================================
...
Node.js version: v9.0.0
...
================================================================================
==== JavaScript Stack Trace ====================================================
...
================================================================================
==== Native Stack Trace ========================================================
...
================================================================================
==== JavaScript Heap and Garbage Collector =====================================
...
================================================================================
==== Resource Usage ============================================================
...
================================================================================
==== Node.js libuv Handle Summary ==============================================
...
================================================================================
==== System Information ========================================================
...
================================================================================

github.com

Performance Timing API

v8.5.0から入ったブラウザでも使われるAPIです。
現在はStability: 1(実験的)です。

Performance Timing API | Node.js v8.9.0 Documentation

詳しくは以下の記事をどうぞ

abouthiroppy.hatenablog.jp

優しいコードの書き方へ

v8.3.0からV8のTurbofan, IgnitionがデフォルトでCrankshaftから移行され、昔のような最適化のためのコードの書き方をしなくても良くなりました。

abouthiroppy.hatenablog.jp

先日のChrome Dev SummitでもV8チームが今後はそのようなアンチパターンをなくしていくと言っています。(つまりどの書き方をしても同じ結果になる)
また、トランスパイルは気をつけるべきです。
Babelにはbabel-preset-envというターゲットバージョンによりトランスパイルをするツールがあります。
babel-preset-envではNodeのバージョンを指定することにより、V8に優しいコードに変換することが可能です。
すべてのコードをトランスパイルするべきではありません。
基本的にトランスパイルされるコードは無駄な処理が多いからです。(これはそのものがエンジン側で実装されてないため)
なので、エンジン側で未実装なもの(e.g. stage-x)だけをトランスパイルするべきです。

{
  "presets": [
    ["@babel/env", {
      "targets": {
        "node": "current"
      }
    }]
  ]
}

github.com

先日、monorepoのbabelへ移行され次のバージョンではscoped packagesになりました🎉

v8::SnapshotCreator

将来的に入るかもしれませんが、今現在、ArrayBuffersに関して議論中です。

github.com

さいごに

今回は、パフォーマンスチューニングをするのに手助けになる手法を数個列挙してみました。
しかし、Node, V8の最適化周りのオプションの話をするとまだたくさんあるとおもいますが一旦このへんで。
詳しくはnode --v8-optionsへ。--print-code, --print-opt-code, --code-comments --track-heap-objects, etc...
その他には、I/O(libuv)とイベントループの理解も大切だと思います。

もしチューニング等でお困りでしたら、Twitterかメールで聞いてくだされば答えれるかもしれません。

また、今月Node学園祭があるので是非お越し下さい:)

nodefest.jp

Chrome Dev Summitに行ってきた話

SFOで行われたChrome Dev Summitに行ってきました。

developer.chrome.com

メインの話はPWA/AMPだった感じだった。
ただの感想文なので特に技術的な話はココでは無しです。

Chrome Dev Summit2017

f:id:about_hiroppy:20171023080036j:plain f:id:about_hiroppy:20171023081100j:plain f:id:about_hiroppy:20171023095031j:plain f:id:about_hiroppy:20171023080825j:plain f:id:about_hiroppy:20171024154819j:plain f:id:about_hiroppy:20171024123337j:plain

よかったこと

学びが多い

PWA/AMPを進めているGoogleだからこそ自分たちが知らないことを話していて勉強になった。
主に、様々な会社の実例が入っていてよかった。
また、WASM, VR, Media等の自分が詳しくない分野の話を聞けて興味がでた。(特にMedia)
これは後ほど別でまとめますのでお楽しみに;)

海外カンファレンスの敷居が案外低かったことがわかった

実は自分は今回が初の海外カンファレンスだったりする。
知らない外国人の方やあまり話せてなかった日本人の方とも話せてよかった。
(ありがとうございます、及川さん、らこさん、armorik83さん、英志さん、etc...:))
知らない人と技術の話をするのは楽しく、様々な分野(VR, etc...)の方と話していた。
また、有名なコミッターの方も多く、そういう人と会える貴重な機会である。(hzoo, sean larkin, etc...)
行ったことがない人は是非行ってみて欲しいと思った、すごい楽しい。(日本には無さそうなお祭り感があった)
あと日本に昔住んでいた外国人の方が多かった(自分は2人と会った)

Googleの有名なエンジニアと話せた

今回は、Alex Russell さんと Paul Irish さんに 30 - 60 minぐらい話せてよかった。
TC39の話、PWAの話、Lighthouseの話、etc...、な話をしました。
TC39のメンバーの方と顔を合わせて喋るのは初めてだった。
こんな貴重な体験、普通ではできないので 宇都宮さん には感謝しかありません。

将来

自分が仕事として何をしたいのかがわかった気がする。
また、今自分に足りないものがわかった。 それをクリアすればいいだなーっていう感じ。
相変わらず海外で働きたいなっていう考えは変わらずです。

OSSへのモチベーション復帰

自分がメンテをしているOSSが発表中に出てくると現実感がある。
頑張ろうって思った。

地域

サンフランシスコ

ちょうど先週はインディアンサマーだったので、雲ひとつなく大変過ごしやすい気候だった。
本来はもっと寒いらしく革ジャンを持っていったのだが一回しか使わなかった。
また、メルカリにはUSオフィスがありお邪魔した。
SFOは危険な地域もあったりするので、行く前に調べたほうがよさそう。(テンダーロインとか)
どこのマンションにもプールやスポーツジムが家についており、最高だった。
ただ、その反面、お風呂とかトイレとかは日本の方が良さそう。

f:id:about_hiroppy:20171021113546j:plain f:id:about_hiroppy:20171025093513j:plain f:id:about_hiroppy:20171024181900j:plain

マウンテンビュー

こちらも雲ひとつなく晴天であった。
みんなTシャツだったりする。
入ったらすぐにGoogle社だったりする。

Google

本当に大学のキャンバスみたいだった。
多国籍だったり、お昼からビーチバレーしたりとかとりあえずすごかった。
こういう風に開放的な方が生産効率は上がるのかな?(メリハリは付きそう)
f:id:about_hiroppy:20171025110427j:plainf:id:about_hiroppy:20171025110538j:plain

最後に

自分がメンテナンスしているOSSがカンファレンスのスライドで発表されるの最高に嬉しいのでこういう報われ方もあるんだなーって思いました。
また、技術的内容などの報告は別ブログや明日のNode学園で話します;)

nodejs.connpass.com

今後も積極的にカンファレンスに参加したいなーって思う。
そして家帰ったら、Google home miniが届いてたのでNature Remoを買ってすべて一括管理するようにしようと準備をし始めた。

未完成のパズルを思い出す

AyoでWorkerの実装が進んでいる

Chrome Dev Summitのため、サンフランシスコで書いています。

さて、最近、Ayo側でWorkerの動きが活発なのでそろそろまとめてみた。
f:id:about_hiroppy:20171023090343p:plain 昨日一日でこれだけ出ている...!

Ayo側

$ ./ayo --version
v9.0.0-pre

github.com

実装者はAnna、過去にNodeにWorkerを入れようという動きもあったため、petkaantonov/io.jsからベースは引用されています。
例えば、initial implementationはこちら

github.com

この間、第一回目のコアチームミーティングが開催されたが、自分は参加してないので何が話し合われたのかわからないです。
後ほど、アップされるとのことなのでそれを待ちます。

github.com

ただ、WorkerがAyoのコアには入っているので、基本的にはStabilityを2まで持っていく方針だと思われます。
Stabilityとは? About this Documentation | Node.js v8.7.0 Documentation

これが最初のNodeとAyoとの技術的な優位性です。

Node側

NodeもAyoからのコミットを入れるかどうかのPRが上がっています。

github.com

ただ今現在、このPRの1つしか存在せず、進んでいないです。
もし、入れたいと思う人がいればここにコメントするとよさそう;)

Workerとは?

独立したスレッドで動作する環境を構築し、それらの間にメッセージチャンネルを構築します。
WorkerはCPU集約型のJavaScriptの操作を実行するのに便利です。
子プロセスやClusterとは違い、それらの間でWorkerはArrayBufferインスタンス間の転送をしたり、 それらの間でSharedArrayBufferインスタンスを共有をすることによりメモリを効率的に共有することが可能です。

Eventの説明は省きます

const worker = require('worker');
const { Worker, isMainThread, postMessage, workerData } = require('worker');

if (isMainThread) {
  module.exports = async function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  postMessage(parse(script));
}

メソッド・変数

isMainThread

Workerスレッド内で実行されているかを返します。
実行されてなければ、trueを返します。

postMessage(value[, transferList])

Workerスレッド内でのみ、使用可能です。
worker.on('message')を経由し、受信される親スレッドのWorkerインスタンスにメッセージを送信します。

threadId

現在のスレッドのIdを返します。

workerData

Workerコンストラクタに渡されるデータの複製を含む任意のJavaScriptデータ。

クラス

MessageChannel

このインスタンスは、非同期の双方向通信チャネルを示します。
独自のメソッドを持ちません。

const { MessageChannel } = require('worker');

const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// prints from L4: received { foo: 'bar' }

上記のコードは、終了しません。(exit)
ずっと待ち続けます。

MessagePort

非同期の双方向通信チャネルの一端を表します。
構造化データ、メモリ領域、および他のMessagePortsを異なるWorkerまたはVMコンテキスト間で転送するために使用できます。 これは、EventEmitterの拡張です。
また、EventTargetsではなくEventEmitterであるMessagePortsを除いて、この実装はブラウザのMessagePortsと同じです。

postMessage(value[, transferList])

Worker

独立したJavaScript実行スレッドを示します。
殆どのAyoのAPIは、この中で実行可能です。

new Worker(filename, options)
filename

filename絶対パスでなければなりません。
options.evaltrueの場合、パスではなくJavaScriptコードを含む文字列となります。

options
  • eval Workerがオンラインになった時に実行されるJavaScript
  • data クローンされ、require('worker').workerDataとして利用可能となるJavaScript値。 複製は、Structured_clone_algorithmと同様に行われできない場合はエラーが飛ばされます(e.g. 関数が含まれている場合)

developer.mozilla.org

  • maxSemiSpaceSize スレッドのヒープのセミスペースのオプションのメモリ制限(MB単位)。
    ほとんどのshort-lived objectsはここに含まれます。

  • maxOldSpaceSize スレッドのメインヒープのオプションのメモリ制限(MB単位)。

const assert = require('assert');
const { Worker, MessageChannel, MessagePort, isMainThread } = require('worker');
if (isMainThread) {
  const worker = new Worker(__filename);
  const subChannel = new MessageChannel();
  worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
  subChannel.port2.on('message', (value) => {
    console.log('received:', value);
  });
} else {
  require('worker').once('workerMessage', (value) => {
    assert(value.hereIsYourPort instanceof MessagePort);
    value.hereIsYourPort.postMessage('the worker is sending this');
    value.hereIsYourPort.close();
  });
}
// prints from L8: received: the worker is sending this
Worker内の大きな違い
  • process.stdinprocess.stdoutprocess.stderrはnullに設定されています
  • domainモジュールは使えません
  • require('worker').isMainThreadfalseに設定されています
  • require('worker').postMessage()が使用可能で、require( 'worker')on( 'workerMessage')がエミットされます
  • process.exit()はプログラム全体を停止せずに単一のスレッドだけを処理し、process.abort()は使用できません
  • process.chdir()及び、グループまたはユーザーIDを設定するプロセスメソッドは使用できません
  • process.env環境変数に対する読み取り専用の参照となります
  • process.titleは変更できません
  • 明示的にWorkerサポートでビルドされていないネイティブのアドオンはロードできません
  • worker.terminate()が呼び出された結果、実行が停止することが可能性があります
  • 親プロセスからのIPCチャネルにはアクセスできません
postMessage(value[, transferList])

require('worker').on( 'workerMessage')を経由し、受信されるメッセージをworkerに送信します。
require('worker').postMessageと違い、require('worker').on( 'workerMessage')を経由することに注意して下さい。
require('worker').postMessageworker.on('message')

terminate([callback])

可能な限り早く、workerスレッド内のすべてのJavaScript実行を止めます。
callbackは終了したら呼び出されます。

threadId

参照されるスレッドIDを返します。

最後に

これらはstabilityが1なのでまだ不安定 + 変更があります。
WorkerはClusterよりもパフォーマンスが良く、利便性があります。
今はAyoだけですが、今後Node側へも入る可能性はあるので注目しておいても良いかもです。

JavaScriptの現状と将来というタイトルで発表してきた

the present and future of JavaScript

情報処理学会若手の会で発表してきました。
三回目の参加で、二回連続30minのセッションをさせて頂きました。
前回は3年前に発表してその頃はまだ学生でした👨‍🎓

実はこの週、スライドにも書いてある通りバンクーバでNode InteractiveとNode関係者があつまるNode.js Collaborator Summitが開かれてました。
そちらの情報は他の方が何かしら報告してくれると思いますので、それを期待しています🙏

さて、それではスライドの内容を少し読み砕きましょう

ECMAScriptとは?

ECMAScriptとは、Ecma Internationalによって管理されるJavaScriptの標準化仕様です。
JavaScriptとは、ECMAScriptに基づく実装を指します。

エディション

そのころに選定された仕様をまとめたものがエディションです。
1997/06に最初のECMAScript 1が出ました。
そして、2015/06 からは毎年でるように変わりました。 (from ES2015)

TC39

ECMA-262とECMA-402を管理します。 JSの構文と国際化APIです。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Language_Resources

ここの委員会メンバーは各ブラウザの主要ベンダーと会社(e.g. Airbnb, FaceBook, PayPal, Bocoup, etc...)です。
また、委員会メンバーの推薦で所属している人もいます。

TC39プロセス

標準化されるまでに5つのプロセスを踏む必要があります。(stage-0 to stage-4)
このstageは二ヶ月に一回あるミーティングにより、変動していき、stage-4に入ったら次のリリースで正式に仕様として組み込まれます。

今年のスケジュールは以下のとおりです。

Dates Location Host
2017-01-24 to 2017-01-26 San Jose, CA PayPal
2017-03-21 to 2017-03-23 Portland, OR Mozilla
2017-05-23 to 2017-05-25 New York, NY Google
2017-07-25 to 2017-07-27 Redmond, WA Microsoft
2017-09-26 to 2017-09-28 Boston, MA Bocoup
2017-11-28 to 2017-11-30 San Francisco, CA Airbnb

各種stageの説明

stage-0(Strawman)

アイディアのフェイズです。
これは委員会メンバーではなくても、提出が可能です。
提出フォームから提出します。
そして、ドキュメントを作り、tc39/proposalsの方へそのリンクを追加するPRを提出する必要があります。

stage-1(Proposal)

ポリフィル、デモがあり、潜在的な問題の特定が必要です。
このステージではチャンピオンと呼ばれるものが必ず必要です。
チャンピオンは、提出者または共同チャンピオン(片方がTC39メンバー)でならなくては次のステージには進めません。

stage-2(Draft)

仕様に含まれる最初のバージョンです。
このstageにいることで仕様として含まれる可能性が高いです。
正式な構文記述の追加が必要です。

stage-3(Candidate)

このプロポーザルの終盤です。
各種ベンダーとユーザからのフィードバックをさらに求めます。
また、正式な仕様のドキュメントは完成しており、TC39のレビュワーとECMAScriptのエディタにより、レビューとサインオフされる必要があります。

stage-4(Finished)

次のリリースで正式な仕様として含まれるでしょう。
test262に合格していて、ブラウザベンダーの2つ以上で実装が組み込まれる必要が必ずあります。
また、ECMAScriptのエディタは仕様書に署名をする必要があります。

test262がBabylonに統合されました。

BabylonとはBabelのJSパーサです。

github.com

今回のMTG(60th)で変更したプロポーザル一覧

約2週間前に変動したプロポーザルの一覧です。

Proposal Stage Description
mport.meta 2 → 3
export ns from 'mod'; 2 → N/A it moved to https://github.com/tc39/ecma262/pull/1005
Array.prototype.flat{Map,ten} 1 → 2
throw Expressions 1 → 2
String.prototype.matchAll 1 → 2
Extensible numeric literals 0 → 1
First-Class Protocols 0 → 1 it was called `Interfaces` in stage-0
JSON superset 0 → 1
nullary coalescing 0 → 1
Partial application 0 → 1
Pipeline Operator 0 → 1
ArrayBuffer.transfer N/A → 0
Builtins.typeOf() and Builtins.is() N/A → 0
Object Shorthand Improvements N/A → 0

github.com

ES2015 - ES2017

スライドを見てください。
また、これは世の中に沢山いい記事があるので省きます。

ES2018

現在、stage-4にあるのは、Template Literal Revisionのみとなっています。

Template Literal Revision

ES2015にも、String Templateの中に、Tagged Templateという書き方がありますが、それには制限がありました。
Tagged Templateというのは、有名なlibraryだとstyled-componentsyo-yoが採用しています。
その制限というのが、Latexwindowsのpath, 8進数のエスケープなどを表現できない(エラーで落ちる)問題です。

const tag = (obj) => ({
  Raw: obj.raw,
  Cooked: obj
});

tag`\u{4B}`; // ES2015 ~
// { Raw: [ '\\u{4B}' ], Cooked: [ 'K' ] }

// ES2018 ~
tag`\uu ${1} \xx`; // a Unicode escape
// { Raw: [ '\\uu ', ' \\xx' ], Cooked: [ undefined, undefined ] }
tag`\100`; // an octal escape

このリリースでは、その制限をなくし、イレギュラーなエスケープはundefinedとして、Cookedに入れられます。

候補

現在、stage-3には以下のプロポーザルがあり、ES2018として組み込まれる可能性が高いです。

面白そうなプロポーザル

興味あるプロポーザルを話しました。
stage-1が多く、Syntaxが変動する可能性があるので注意してください。
Binary ASTは他の場所で話します😌

Class Fields

class Counter extends HTMLElement {
  x = 0;  // public fields
  #y = 0; // private fields

  constructor() {
    super();
    this.onclick = this.clicked.bind(this);
  }

  clicked() {
    this.x++;
    this.#y++;
    window.requestAnimationFrame(this.render.bind(this));
  }

  render() {
    this.textContent = this.#y.toString();
  }
}

Promise.prototype.finally

let finished = false;

fetch()
  .then((res) => {
    // finished = true;
  })
  .catch((err) => {
    // finished = true;
  })
  .finally(() => {
    finished = true;
  });

同じ処理をthenとcatchで書かなくて良くなりました。

Optional Chaining

abouthiroppy.hatenablog.jp

Pipeline Operator

abouthiroppy.hatenablog.jp

Partial Application Syntax

function add(x, y) { return x + y; }

// before
const addOne = add.bind(null, 1); // this, the left(x = 1), the right(y = undefined)
addOne(2); // 3

// after
const addOne = add(1, ?); // apply from the left(x)
addOne(2); // 3

const addTen = add(?, 10); // apply from the right(y)
addTen(2); // 12

const f = (...x) => x;
const g = f(..., 9, ...);
g(1, 2, 3); // [1, 2, 3, 9, 1, 2, 3]

const res = a |> f(?, 1) |> g(?, 2); // const res = g(f(a, 1), 2);

今まではES5のbindで部分適用していましたが、これからは?がある場合に固定化されます。
また、spread operatorでも展開できます。

github.com

Promise.try

bluebirdと同じ、tryです。

function getUserById(id) {
  return Promise.try(() => {
    if (typeof id !== 'number') {
      throw new Error('id must be a number');
    }

    return db.getUserById(id);
  });
}

Temporal

日付の計算等の扱いが簡単になります。

let myCivilDate = new CivilDate(2016, 2, 29);
let newCivilDate = myCivilDate.plus({years: 1, months: 2});
//results in civil date with value 2017-4-28

github.com

まとめ

  • ECMAScriptは毎年更新されます
  • 誰でもプロポーザル出せるぞ!!
  • 次のリリース(ES2018)は2018/06です!

宣伝

2017/11/25 - 26 で東京Node学園祭2017をやるので是非来てくださいね;)

nodefest.jp

The End

それではJavaScriptライフを楽しんで!😎

github.com

Pipeline Operatorがstage-1へ

github.com

先月のTC39のMTGでstage-1になりました。

F#, OCaml, Elixir, Elm, Julia, Hack, LiveScriptなどと似たような機能を提供します。

function doubleSay (str) {
  return str + ", " + str;
}

function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}

function exclaim (str) {
  return str + '!';
}

let result = exclaim(capitalize(doubleSay("hello"))); // "Hello, hello!"

let result = "hello"
  |> doubleSay // ここの引数が `hello`になる
  |> capitalize
  |> exclaim; // "Hello, hello!"

上記のコードを読めばわかりますが、ネストされた関数を読みやすくします。

例えば、引数を複数持つ関数でPipeline operatorを使いたい場合は以下のようになります。

function double (x) { return x + x; }
function add (x, y) { return x + y; }

function boundScore (min, max, score) {
  return Math.max(min, Math.min(max, score));
}

let newScore = boundScore(0, 100, add(7, double(person.score)));

let person = { score: 25 };
let newScore = person.score
  |> double // ここの引数が25
  |> _ => add(7, _) // `_`はdoubleの戻り値
  |> _ => boundScore(0, 100, _); // 57 // `_`はaddの戻り値

自分のページを更新した

about-hiroppy.com

一年ぶりぐらいに更新した。
好きなサイトの色とフォントを参考にした。

sweetpackを使ったので基本設定ファイルは書かなくてよかった。

postcss-forとpostcss-randomを初めて使ったが、混ぜると大変だった。。
forのスコープ内で変数宣言時にrandom使うとそこで確定されると思ったらコールされるたびに値が変わるのなんでだろう。。

@for $i from 1 to 10 {
  $delay: random(0, 30)s;

  .a {
    animation-delay: $delay;
  }

  .b {
    animation-delay: $delay;
  }
}

.a.bのanimation-deplayの値変わるんだよね。。
これってつまり、変数化する必要がないじゃん!!
なので共通で使いたかった部分を変数化したつもりが使えなくて共通を出さない実装に変えざる負えなかった😓

scssはたしか変わらない気がする。

https化を次はする。

おわり