kidoOooOoooOOom

IT系で開発やってます

Effective JavaSceipt 読書メモ 項目18~21 高階関数とか

第3章 関数の扱い

項目18 関数、メソッドコンストラクタの、呼び出しの違いを理解する

JSでは、関数・メソッドコンストラクタは全て関数(function)を用いた3種類の利用パターンとしてとらえる。
下記のobjは hello というメソッドを持つ。JSではメソッドは単なる「関数であるオブジェクトプロパティ」にすぎない。

var obj = {
  hello: function() {
    return "hello " + this.username;
  },
  username: 'totakeke'
};

var obj2 = {
  hello: obj.hello,
  username: 'peritutu'
};

console.log(obj2.hello()); // hello peritutu

obj2.hello()の呼び出しの結果、objではなくobj2の username が this参照されている。JSでは、thisに結合される値は、その呼び出しのレシーバと呼ばれる。

thisを使う関数を、メソッドではなく関数として呼び出すのはあまり推奨されない。
下記のコードは、ES5のstrictモードでは this がundefinedに結合されるため、実行時エラーになる。
また、非strcitモードではthisがグローバルオブジェクトになり、byebye undefinedが出力される。

function byebye() {
  return "byebye " + this.username;
}

console.log(byebye());

気をつけるべし。

項目19 高階関数を快適に使えるようになろう

高階関数とは、「ほかの関数を引数として受け取るか、結果として関数を返す関数」とのこと。高階関数に引数として渡す関数はしばしばコールバック関数と呼ばれる。
たとえば文字列を大文字に変換するという単純な例を、map関数にコールバック関数を渡して実行する例を下記に示す。

var names = ['totakeke', 'takashishi'];
var upper = names.map(function(name) {
  return name.toUpperCase();
});
console.log(upper); // [ 'TOTAKEKE', 'TAKASHISHI' ]

高階抽象には多くのメリットがある。実装にトリッキーな部分があれば、それらをまとめて高階関数の実装に局所化(localize)できる。自分が同じパターンを繰り返し書いていると気づいたら高階関数に切り替えることを学べば、よりコードが簡潔になり、生産性が高まる。

項目20 カスタムレシーバ付きでメソッドを呼び出すにはcallを使う

項目18で述べた通り、通常、関数またはメソッドのレシーバは呼び出し側の構文によって決まる。
ただし、ときにはカスタムレシーバをつけて関数を呼び出す必要が生じる。こういった場合、カスタムレシーバを提供するための組み込み関数 call()を使えばよい。

f.call(obj, arg1, arg2, arg3);

上記の記述によって、objをレシーバとしてf関数が呼び出される。

またcallメソッド高階関数を定義するときにも便利に使える。高階関数で一般的なイディオムのひとつは、オプションの引数によって関数呼び出しのレシーバを提供するものである。
例えば下記のコードは、クライアントがコールバック用にレシーバを提供できる高階関数をcallによって定義している。

var table = {
  entries: [],
  addEntry: function(key, value) {
    this.entries.push({key: key, value: value});
  },
  forEach: function(f, thisArg) {
    var entries = this.entries;
    for (var i = 0, n = entries.length; i < n; i++) {
      var entry = entries[i];
      f.call(thisArg, entry.key, entry.value, i);
    }
  }
}

table1.forEach(table2.addEntry, table2); // table1のentriesをtable2にコピー

項目21 いくつでも引数を取れる関数を呼び出すには apply を使おう

callメソッドの親戚で、引数を配列として受け取りたい場合はapplyメソッドを使うとよい。thisを第1引数で結合できるのはcallメソッドと同じである。

参考になるページ:

applyとcallの使い方を丁寧に説明してみる - あと味