ES6 In Depth: Arrow functions

これは2015年6月4日にJason Orendorff氏によって投稿されたES6 in Depthの翻訳です。

ES6 In Depth はECMAScript標準, ES6でjavascriptプログラミング言語に加えられた機能のシリーズです。

Arrowsは初期からJavascriptの一部とされて進められてきました。最初のJavaScriptチュートリアルは”インラインスクリプトをHTMLコメントで囲め”と助言していました。これはjavascriptをサポートしていないブラウザが誤ってjsのコードをテキストとして表示されるのを防ぐためです。あなたはこんなコードを書いたと思います。

<script language="javascript">
<!--
  document.bgColor = "brown";  // red
// -->
</script>

古いブラウザには2つのサポートしていないタグとコメントに見えます。新しいブラウザのみjsコードと解釈します。

この妙なハックのためには、あなたのブラウザのJavascriptエンジンで<!--という文字列を一行コメントとして扱える必要があります。ジョークじゃないです。これは当初から今日におけるまで言語の一部として寄り添っています。そして、インライン<script>だけではありません、JSのコードのどこでも動きます。さらにNode.jsでさえも動きます。

たまたまこのスタイルのコメントは初めてES6で標準化されました。しかしこれは今日話す内容のarrowではありません。

arrow sequence –>もまた一行コメントとして表示(解釈)されます。奇妙なことにHTML文字列では–>の”前”がコメントで、JSでは行の残り、–>”後”がコメントです。

これは奇妙です。このarrowは行の開始に現れた時のみコメントとして適用されます。これは他の文脈では–>はJSの演算子、”goes to”演算子として働きます!

function countdown(n) {
  while (n --> 0)  // "n goes to zero"
    alert(n);
  blastoff();
}

このコードは本当に動きます。ループはnが0になるまで回ります。これはES6標準ではありませんが、
少しの間違いとともに複数のおなじみの機能が入り込んでいます。あなたはこれがわかりますか?
普通、パズルの答えはStack Overflowで見つかります。
もちろんここにはイコール大なり、<=演算子もあります。多分あなたはJSコードのなかで画像のような矢印をもっと見つけることができます、しかし立ち止まって矢印が欠落していることを観察してみましょう。

<!-- single-line comment
--> “goes to” operator
<= less than or equal to
=> ???

 

=>では何が起こるのでしょうか?今日、その謎を解決していきましょう。

最初に、functionについて少し話しましょう。

Function表現はどこにでも

Javascriptの楽しい特徴として、あなたが関数を必要とした時、いつでも動いているコードの中でfunctionとタイプするだけでいいことです。

例としてあなたは、ブラウザにユーザーが特定のボタンをクリックした時、何をしたいか伝えたいとします。こんなコードを書きますよね?

$("#confetti-btn").click(

jQuery’s .click() は引数を一つ取ります:functionです。あなたはここにfunctionとタイプするだけで良いのです。

$("#confetti-btn").click(function (event) {
  playTrumpet();
  fireConfettiCannon();
});

このようなコードを書くことは私達にとってとても自然なことです。なのでJavascriptがこの種類のプログラミングをはやらせる前は、多くの言語はこの機能を持っていませんでした。もちろんLispはfunction表現を持っていますが、それはラムダ式と呼ばれるものです。1958年のことです。しかしC++やPython,C#やJavaは長い間この機能を持っていませんでした。

でももう違います。上記4つともラムダ式を持っています。新しい言語は普遍的に組み込まれたラムダ式(注:ビルドイン関数ではない)を持っています。そして以前のJSプログラマが作った大胆なライブラリは深くラムダ式に依存し、この機能の採用を広く助けました。

これはちょっと残念です。その後、上の4つの全ての言語に私はメンションをして JavaScriptのラムダは、消滅しました。

// A very simple function in six languages.
function (a) { return a &gt; 0; } // JS
[](int a) { return a &gt; 0; }  // C++
(lambda (a) (&gt; a 0))  ;; Lisp
lambda a: a &gt; 0  # Python
a =&gt; a &gt; 0  // C#
a -&gt; a &gt; 0  // Java

あなたの矢筒に新しい矢を

ES6はfunctionに新しい記法を導入しました。

// ES5
var selected = allJobs.filter(<strong>function (job) {
  return job.isSelected();
}</strong>);

// ES6
var selected = allJobs.filter(<strong>job =&gt; job.isSelected()</strong>);

あなたが引数が一つでSimpleな関数を必要とする時、新しいarrow function記法はIdentifier =&gt; Expressionと書くだけで済みます。
あなたはfunctionやreturnをスキップ出来ます。同様に、幾つかの括弧、中括弧、そしてセミコロンも。
個人的にこの機能は素晴らしい物だと思っています。 functionとタイプする必要がないことは私にとって重要です。なぜなら私は代わりにfunctoinとタイプしてしまい、戻って修正する必要があるからです。

複数の引数 (もしくは引数なし、可変長引数やデフォルト引数、 破壊的代入)を取るfunctionを書くためには、複数の引数を括弧で囲むことが必要です。

// ES5
var total = values.reduce(<strong>function (a, b) {
  return a + b;
}</strong>, 0);

// ES6
var total = values.reduce(<strong>(a, b) =&gt; a + b</strong>, 0);

私はこれをとても素晴らしい物だと思います。

Arrow functionは美しい機能的なツールと一緒に Underscore.js and Immutable。などのライブラリから提供されています。実際に、この例は全てES6で書かれている Immutable’s documentation に載っています。なのでこれら多くはすでにarrow functionを使っています。

not-so-functional settingsとは?Arrow functionはexpressionの代わりにstatementsのブロック含むことが出来ます。以前の例を思い出して下さい。

// ES5
$("#confetti-btn").click(<strong>function (event)</strong> {
  playTrumpet();
  fireConfettiCannon();
});

ES6ではこのようになります。

// ES6
$("#confetti-btn").click(<strong>event =&gt;</strong> {
  playTrumpet();
  fireConfettiCannon();
});

わずかな改善です。このコードの効果は Promises を使用するともっと劇的に上昇します、 }).then(function (result) {のようなラインを重ね合わせることが出来ます

ブロック付きarrow functionは自動で値を返さないことに注意して下さい。その場合はreturn文を使用します。

arrow functionを使用しplainなオブジェクトを作る時に警告があります。オブジェクトは括弧で囲まなければいけません。

// create a new empty object for each puppy to play with
var chewToys = puppies.map(puppy =&gt; {});   // BUG!
var chewToys = puppies.map(puppy =&gt; ({})); // ok
//訳者解説:引数を取らず、ブロックステートメント({})を使わずにplainなオブジェクトを返す時のコードは括弧が必要です。

不幸にも、空のオブジェクト{} と空のブロック {}はちょうど同じです。ES6のルールに{arrowの直後にある{はブロックの開始として扱われる、オブジェクトで始まってはいけないという物があります。したがってpuppy => {}というコードはundefinedを返すarrow functionとして静的にインタプリタされます。

さらに混乱させるのが、{key:vaue}のようなオブジェクトリテラルはlabelを含んだブロックステートメントと同じということです。これはあなたのJSエンジンにはどのように見えるでしょうか?幸運にも{は曖昧な文字ですなのでオブジェクトリテラルを括弧で囲むというトリックを覚えておくだけで通用します。

Thisって何?(原文:What’s this?)

従来のfunctionとarrow functionには微妙な違いが存在します。Arrow functionは自分自身のthisを持ちません!arrow function内の値thisはいつもその外側のスコープの値を継承します。

試して実際に何を意味するのか理解する前に、すこし復習してみましょう。

JavaScriptでthisはどのように動作する?この値はどこから来てるの?この疑問を短い答えで返すことは出来ません。もしこの答えが頭のなかで出せるなら、それはあなたが長い間thisと一緒にいたからでしょう!

この疑問の理由はだいたいfunction functionがthisの値を自動で受け取ることに由来します。あなたはこんなハックを書いたことがありますか?

{
  ...
  addAll: function addAll(pieces) {
    var self = this;
    _.each(pieces, function (piece) {
      self.add(piece);
    });
  },
  ...
}

ほら、あなたが内部関数に書けばいいものはthis.add(piece)だけです。不運にも、内部関数は外部のfunctionのthisの値を継承しません。inner functionの内部ではthisはwindowやundefinedです。一時的な値selfは外側の値thisを内部に密輸します。(他の方法としてはbindを内部関数に使う方法があります。どちらの方法も綺麗です)

ES6ではもしあなたが下のルールを使用しているならだいたいthisハックは必要ありません。

  • object.method()記法を使って呼ばれるメソッドでnon-arrow function(いわゆるレキシカルなfunction)を使用している。これらのfunctionは意味のあるthisをcallerから受け取ります。
  • それ以外ではarrow functionを使用している。
// ES6
{
  ...
  addAll: function addAll(pieces) {
    _.each(pieces, piece =&gt; this.add(piece));
  },
  ...
}

ES6バージョンではaddAllメソッドはthisをcaller(function.caller)から受け取ります。内部関数はarrow functionです、なのでthisを外部のスコープから継承します。

ボーナスとしてES6はオブジェクトリテラルのメソッドを短く書く方法が提供されます!なのでこのコードは更にシンプルにすることが出来ます。

// ES6 with method syntax
{
  ...
  addAll(pieces) {
    _.each(pieces, piece =&gt; this.add(piece));
  },
  ...
}

メソッドとarrowsの間では、私は”functoin”と書くことはもうないと思います、これはいい考えです。

それらは arrow と non-arrow functionsのもっとマイナーな違いとしてarrow functionは自分自身のargumentsを受け取りません。

もちろん、ES6ではあなたは多分デフォルト引数や可変長引数を使用することでしょう。

Using arrows to pierce the dark heart of computer science

私達はarrow functionの実践的な使用方法について話してきました。これらのもっといろんなユースケース:ES6のarrow functionのlearning toolとして、計算科学の深いところまで話そうかと思います。あなたが参加するかどうかはアナタ次第です。

1936年にAlonzo ChurchとAlan Turingは独自で強力な計算モデルを開発しました。Turing はa-machinesと呼びました。しかし、人々はすぐにこのモデルをチューリングマシンと呼び始めました。Churchは代わりにfunctionについて書きました。彼のモデルは λ-calculus.と呼ばれています。この成果はLispでLAMBDAという文字が使われているのに由来します。

しかしラムダ式とは何でしょうか?計算モデルとは何を意味するのでしょう?

これを幾つかの言葉で説明するのは難しいですが、挑戦してみました:ラムダ式は最初のプログラミング言語です。これはプログラミング言語として設計されていません。_結局、10、20年経ってもプログラム内蔵コンピューターは来ませんでした。_しかし、あなたがやりたいと願った全ての計算の表現出来る言語はこれ以上無いほどSimpleで、必要最小限の、純粋な数学的アイデアです。Churchはcomputation in generalを証明するためにこのモデルが必要でした。

そして彼は彼のシステムにおいてfunctionのみが必要とされることを発見しました。

彼の主張がいかに特別だったかがわかります。オブジェクトなし、配列なし、数字なし、if文もwhileループもセミコロンもassignmentも、論理式もイベントループもです。これは全てのJavascriptが出来る全ての種類の計算をfunctionのみを使用して一から設計することが可能です。

これがChurchのラムダ式表記法を使い数学者が書くことが出来るタイプの”プログラム”の例です

fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v)))

これと同等のJavascriptのfunctionです。

var fix = f => (x => f(v => x(x)(v)))
               (x => f(v => x(x)(v)));

これはJavaScriptにはλ-calculusの実装が含まれていて実際に実行できます。JavaScriptのラムダ式です。

このAlonzo Churchとその後の研究者がラムダ式で何をしたか、そしてそれがどのように大抵のメジャーなプログラミング言語に入り込む(実装される)かはこのブログの範囲を超えてしまいます。 しかしもしあなたがコンピューターサイエンスの基版に興味があるなら、 もしくはあなたがちょうどfunctionのみでループや再帰のようなこと出来る言語を見たいなら、あなたは 雨の午後を費やして、 Church numerals and fixed-point combinatorsを探して、あなたのFirefoxコンソールや Scratchpadで試すことが出来ます。ES6のarrowsの他の強みとともに。 JavaScriptはラムダ式を研究するための最良の言語です。

いつArrowを使えるの?

firefoxにおけるES6のarrow functionsは私が実装しました 。2013年頃です。 Jan de Mooijは実装を早くしてくれました。 パッチを投げてくださったTooru Fujisawa氏とziyunfei氏に感謝します。

Arrow functionsはMicrosoft Edgeのプレビューリリースでも実装されています。もしarrowをWebで使うことに興味があり、今すぐ使いたいなら BabelTraceurTypeScriptを使うことが出来ます。

次のトピックはES6の中でも奇妙なものです。typeof x が全く新しい値を返してきます。私達は尋ねます。なぜこれはstringじゃないの?私達は”同じ”の意味に悩むでしょう。これらは奇妙です。次の週は私達と一緒にES6のシンボルについて見ていきましょう。

1 件のコメント

  1. lv7777 :

    レビューしていただけるとありがたいです。