jQueryのコールバックの中でthisを使うメソッドを呼びたい

いいタイトルが思いつかなかったのですが、javascriptの `method.call` の使い方がようやく分かったので備忘録。

# tl;dr
```js
//インフォメーション用のDomのテキストを変更するメソッド
function chMsg(text){...}

$('input.responsible')
.on('click', function(){ chMsg('click'); })
.on('change', function(){ chMsg('update!'); })
.on('input', function(){ chMsg('edit...'); });
```

みたいなことがしたいときに、`chMsg`に`this`を渡すには、`call`を使って以下のように書けば良さそうです。

```js
function chMsg(text){
  $(this).parent().find('.infomation').text(text);
}

$('input.responsible')
.on('click', function(){ chMsg.call(this, 'click'); })
.on('change', function(){ chMsg.call(this, 'update!'); })
.on('input', function(){ chMsg.call(this, 'edit...'); });
```

# 経緯

※面倒くさいので、ここからはCoffeeScriptで記述

「テキストフィールドの入力内容をチェックしたい。間違ってるとテキストフィールドが赤枠になるとか」
「やりましょう」

```coffeescript
$(text_field).on 'change', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
```

「これだと入力後じゃなくて入力と同時にチェックしてよ」
「たしかに」

```coffeescript
$(text_field).on 'input', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
```

「あー。入力後にチェック失敗したなら、何処かにメッセージ出すと親切だよね」
「ふむ」

```coffeescript
$(text_field).on 'input', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
.on 'change', ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
    $self.parent.find('span').text('いいよ！')
  else
    $self.addClass('no-good')
    $self.parent.find('span').text('おこだよ')
```

（クラスを付けたり消したりするところは、外に出したいな…）

```coffeescript
inputValid ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
  
$(text_field)
.on 'input', inputValid
.on 'change', ->
  inputValid
  if myValidate this.value
    $self.parent.find('span').text('いいよ！')
  else
    $self.parent.find('span').text('おこだよ')
```

と、ここまで来ると、changeイベントの時に外に出した`inputValid`の`this`でテキストフィールドの値を取得できていないことによる不都合が置きます。

いくつかのjQueryプラグインを参考にすると、以下のように記述して、サブメソッドにイベントの`this`を渡しているようでした。

```coffeescript
sub_method = ->
 # thisを使った処理

$(targets).on 'change', ->
  # thisを渡すためにeachを使う
  $(this).each sub_method
```

このような書き方だとChrome / Firefoxでは動作するのですが、何故かIE8ではjQuery内部でエラーが起きて動かない…jQuery内部まで見て原因を調べるのも手間なので別の方法を探りました。

試しにjQuery.fn.eachについて調べてみると以下のようになっています。
（元のコードはjavascriptです）

```coffeescript
# jQuery.fn.each
(callback, args)->
  jQuery.each( this, callback, args )

# jQuery.each
(obj, callback, args) ->
  value = undefined
  i = 0
  length = obj.length
  isArray = isArraylike(obj)

  # (※1) argsがある場合の処理..今回は関係ない
  if args
    if isArray
      while i < length
        value = callback.apply(obj[i], args)
        break  if value is false
        i++
    else
      for i of obj
        value = callback.apply(obj[i], args)
        break  if value is false
  
  # A special, fast, case for the most common use of each
  else
    if isArray
      while i < length
        value = callback.call(obj[i], i, obj[i]) # ←これだ！
        break  if value is false
        i++
    else
      for i of obj
        value = callback.call(obj[i], i, obj[i])
        break  if value is false

  # return
  obj
```

なるほど。eachでは結局、`call`をつかってコールバックを動かしているようですね。
というわけで、以下のように書いてみます

```coffeescript
inputValid ->
  $self = $(this)
  if myValidate $self.val()
    $self.removeClass('no-good')
  else
    $self.addClass('no-good')
  
$(text_field)
.on 'input', inputValid
.on 'change', ->
  inputValid.call(this)  # thisを渡して動かす
  if myValidate this.value
    $self.parent.find('span').text('いいよ！')
  else
    $self.parent.find('span').text('おこだよ')
```

ちゃんとIE8でも動きました。よく考えたら、eachと同じことをしているのに、なんで動いたのか謎です。ループなどの他の部分でひっかかっていたのかな…？

# ところでjQuery.fn.eachに第二引数を渡せたんだ

上で引用しているjQuery.eachのコードだと、第二引数に`args`がある場合、jQuery.eachの(※１)の部分が呼ばれるようです。

```coffeescript
$('li').each (a,b,c)->
  console.log this #=> <li ...>, <li ...>, <li ...>,...
  console.log a #=> 0,1,2,...
  console.log b #=> <li ...>, <li ...>, <li ...>,...
  console.log c #=> undefined , undefined , undefined ...


$('li').each (a,b,c)->
  console.log this #=> <li ...>, <li ...>, <li ...>,...
  console.log a #=> 'hello', 'hello', 'hello', ...
  console.log b #=> 100,100,100,...
  console.log c #=> Window, Window, Window,...
, ['hello', 100, this]
```

使いドコロが分かりません…
古いバージョンと互換性を保つために残っている使い方なのかとも思ったのですが、どうもjQueryの内部でjQuery.fn.loadというAjax関連のメソッドで第二引数を活用しているっぽいです。
jQueryのAPIドキュメントには第二引数を渡す使い方は書かれていないので、非推奨なのかな…？

なおjQueryの軽量クローンであるZeptoでは、第二引数をしたりはできなさそう。

# 参考

- [applyとcallの使い方を丁寧に説明してみる - あと味](http://taiju.hatenablog.com/entry/20100515/1273903873)
- [JavaScriptの「this」は「4種類」？？ - Qiita](http://qiita.com/takeharu/items/9935ce476a17d6258e27)
- [Javascriptのcall/apply関数のプロっぽい使い方 〜 JSおくのほそ道 #014 - Qiita](http://qiita.com/setzz/items/155af03120a330fd4437)
- [API Documentation - jQuery](http://api.jquery.com/each/)