前回に引き続きTypeScriptの話
1回目
http://rei19.hatenablog.com/entry/2013/07/14/183311
2回目
http://rei19.hatenablog.com/entry/2013/07/23/023008
今回は関数についてです。
関数定義の基本
変数に型を付けられるようになったことで、関数の引数と返り値の型定義も出来るようになりました。
TypeScript
function funcTest(arg1 : string, arg2? : number) : boolean{ console.log(typeof arg1 + ' : ' + arg1); console.log(typeof arg2 + ' : ' + arg2); console.log(typeof this); return true; } // ①引数全指定 console.log(funcTest('ひきすう', 0)); 出力結果 // string : ひきすう // number : 0 // object // true // ②引数必須のみ指定 console.log(funcTest('ひきすう')); // 出力結果 // string : ひきすう // undefined : undefined // object // true // ③データ型の違う引数を指定 console.log(funcTest('ひきすう', '0')); // コンパイルエラー
function funcTest(arg1, arg2) { console.log(typeof arg1 + ' : ' + arg1); console.log(typeof arg2 + ' : ' + arg2); console.log(typeof this); return true; } console.log(funcTest('ひきすう', 0)); console.log(funcTest('ひきすう'));
では、言語仕様の解説をします。
まず、「変数名 : 型」 で変数にデータ型を定義出来るようになったのは前回書いた通りです。
関数を呼びだしているところの③をみると2個目の引数にstringを渡しているので型変換でコンパイルエラーとなります。
2個目の引数の後ろに「?」がくっついていますが、これはオプショナルの引数であるという意味です。
関数を呼び出しているところの②を見て下さい。1個目の引数だけで関数を呼んでいますがコンパイルエラーにはなりません。既存のJavaScriptの仕様で定義されていない変数はundefinedになります。
なお初期値を設定する場合は「arg1 : string == 'でふぉると'」となりますが、「arg1? : string == 'でふぉると'」とするとコンパイルエラーになります。これは初期値が設定されているのでオプショナルのわけがなく、不要なコードであるのでエラーになるということでしょう。
次に関数の引数の定義の後の「: boolean」ですが、これは関数の返り値の型を定義しています。
サンプルの関数は最後に「return true」をしていますが、文字列やNumberを指定した場合はコンパイルエラーとなります。
アロー関数の登場
TypeScriptのアロー関数は無名関数の構文で本家のJavaScriptはES6から投入されるようですが、TypeScriptでは先行して導入されています。
先ほどの関数をアロー関数で書き換えてみます。
TypeScript
var arrowTest = (arg1 : string = '11', arg2? : number) : boolean => { console.log(typeof arg1 + ' : ' + arg1); console.log(typeof arg2 + ' : ' + arg2); console.log(typeof this); return true; } console.log(arrowTest('ひきすう', 0)); console.log(arrowTest('ひきすう'));
var _this = this; var arrowTest = function (arg1, arg2) { if (typeof arg1 === "undefined") { arg1 = '11'; } console.log(typeof arg1 + ' : ' + arg1); console.log(typeof arg2 + ' : ' + arg2); console.log(typeof _this); return true; }; console.log(funcTest('ひきすう', 0)); console.log(funcTest('ひきすう'));
アロー関数の特徴は2つです。
・new できない。
・thisの固定化。
new できないということはコンストラクタではないということです。通常のfunction演算子で作られたfunctionオブジェクトは関数でもあり、オブジェクトのプロトタイプたるコンストラクタでもあるのですが、=>で作られた関数はコンストラクタとしての機能は持っていないということです。
問題はthisについて。JavaScriptにおけるthisは呼ばれるところで参照先が異なります。
※ここで触れるとちょっと長くなるので割愛。このへん読むと面白いです。
http://qiita.com/vvakame/items/74005adacc0e8e2a3cab
http://qiita.com/KDKTN/items/0b468a07410d757ac609
サンプルコードに戻ってみます。構文としては「(引数) => {処理}」で、アロー関数のオブジェクトが返ります。そしてコンパイル後のコードをみると1行目に「var _this = this;」という記述があります。これが最大の特徴です。上のコードだとちょっとわかりづらいので、利点がわかるように書き直してみます。
var obj = { cnt : 0, arrowTest : function(){ // 普通の関数をリテラルのプロパティとしてセット if(typeof this.cnt == 'number'){ this.cnt++; } console.log(this.cnt); }, arrowTest2 : () =>{ // アロー関数をリテラルのプロパティとしてセット if(typeof this.cnt == 'number'){ this.cnt++; } console.log(this.cnt); }, arrowTest3 : function(){ // プロパティ内に無名関数を定義 (function(){ if(typeof this.cnt == 'number'){ this.cnt++; } console.log(this.cnt); })(); }, arrowTest4 : function(){ // プロパティ内にアロー関数を定義 (() => { if(typeof this.cnt == 'number'){ this.cnt++; } console.log(this.cnt); })(); } } obj.arrowTest(); obj.arrowTest2(); obj.arrowTest3(); obj.arrowTest4();
このコードですが、順当に全部動くと1 2 3 4と表示されるように見えますが、実際には以下の通り出力されます。
1
undefined
undefined
2
コンパイル後のコードを見てみましょう。ちょっと解説を加えました。
var _this = this; // arrowTest2でアロー関数を使ったため定義時点のthisを_thisとして退避 var obj = { cnt: 0, arrowTest: function () { // ここは普通にobj=thisなのでthis.cntは有効 if (typeof this.cnt == 'number') { this.cnt++; } console.log(this.cnt); }, arrowTest2: function () { // ここはアロー関数で定義されたのでコンパイル後はthisはオブジェクト外の_thisに置き換わっている if (typeof _this.cnt == 'number') { _this.cnt++; } // _thisはcntを持っていないのでundefined console.log(_this.cnt); }, arrowTest3: function () { // ここでのthisはobjではなくarrowTest3内の無名関数を指しているためcntは無い (function () { if (typeof this.cnt == 'number') { this.cnt++; } console.log(this.cnt); })(); }, arrowTest4: function () { // ここではobjを指しているthisを _thisに退避して、無名関数内では退避した_thisを指しているためcntが有効。 // ここでのthis→無名関数 // ここでの_this→obj var _this = this; (function () { if (typeof _this.cnt == 'number') { _this.cnt++; } console.log(_this.cnt); })(); } }; obj.arrowTest(); obj.arrowTest2(); obj.arrowTest3(); obj.arrowTest4();
という感じで実行時に決まるはずのthisをallow functionを使うと固定できるという仕組みです。
コンパイル前後のコードを比較するとわかりやすいかなと思います。
JavaScriptだとコンパイル後のコードのようにthisをselfとか_thisとかに退避するのが慣例でしたが、アロー関数を使うと幸せになれますね!
次回に続く
- 作者: 栗林健太郎,柴田博志,はまちや2,常松伸哉,黒田良,川添貴生,安宅啓,松下雅和,桑野章弘,Jxck,伊藤直也,佐藤鉄平,登尾徳誠,中川勝樹,奥野幹也,近藤宇智朗,堀江幸紀,後藤秀宣,渡邊恵太,中島聡,A-Listers,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2013/06/22
- メディア: 大型本
- この商品を含むブログ (8件) を見る