7cc@はてなブログ

JavaScriptとかとか

addEventListenerのhandleEventについて

オブジェクト(指向)で使うようです。

syntax

プロパティにhandleEventがあるオブジェクトを第二引数に渡す。
handleEventの値はfunction

// 変数に入れて
var foo = {
  v: 1,
  handleEvent: function(){ alert(this.v) }
}
document.addEventListener("click", foo)
document.removeEventListener("click", foo) // removeできる

// 直接
document.addEventListener("click", {
  v: "hello",
  handleEvent: function(){
    alert(this.v)
    document.removeEventListener("click", this)
  }
})

this.handleEventが参照されるので、継承では近いものになる。

function Foo(){}
Foo.prototype.handleEvent = function(){
  console.log(1)
}

var foo = new Foo
document.addEventListener("click", foo, false)

// ↑ Foo.prototype.handleEventが呼ばれる
// ↓ foo.handleEventが呼ばれる

foo.handleEvent = function(){
  console.log(2)
}

使う利点は何か

thisはイベントリスナ内でevent.currentTargetを指す。
handleEventではオブジェクトに固定される

handleEventを使わない場合

var o = {
  str: "hello",
  foo: function(){
    alert(this.str)
  }
}

o.foo() // "hello"
document.addEventListener("click", o.foo) // undefined

上の場合はo.fooのthisがイベントを設定したdocumentになり、document.fooが参照されるのでundefinedに。

handleイベントを使った場合

var obj = {
  str: "hello",
  handleEvent: function(){
    alert(this.str)
  }
}

o.handleEvent() // "hello"
document.addEventListener("click", obj) // "hello"

thisの呼び出しがobjに固定された。

使う必要は無い

handleEventを使わなくても同じことはできる。
Array#mapみたいなあれば便利だけど、無いなら無いでどうにかなる系だと思う。
thisを固定するひと手間がかかるので、使うにこしたことは無いのだけれど。

必要かという点で見れば、必要でないメソッドは沢山あるので深く考えない方が良いと思う´ω`)

オブジェクトの例

var obj = {
  str: "hello",
  fn: function(){
    alert(this.str)
  }
}

// thisの呼び出しを固定するひと手間
var listener = function() {
  obj.fn()
}
document.addEventListener("click", listener, false)
document.removeEventListener("click", listener, false)

MDNに載っている例のせいかbindと比較されることが多いけれど、
bindでもイベントリスナを変数に代入すればremoveできる。

コンストラクタの例

3つの異なる書き方を比較する。どれも同じことをしている。

handleEvent + コンストラクタ

function Foo(element, str) {
  this.str = str
  this.element = element
}
Foo.prototype.handleEvent = function(evt) {
  alert(this)
}
Foo.prototype.init = function() {
  this.element.addEventListener('click', this)
}
Foo.prototype.cancel = function() {
  this.element.removeEventListener('click', this)
}

var foo = new Foo(document, "hello")
foo.init()
foo.cancel()

Foo.prototype.handleEvent = function(){} // 全体のcallbackを書き換え
foo.handleEvent = function(){} // 個別に書き換え

記述が一番短い

function + コンストラクタ

function Foo(element, str) {
  this.element = element
  this.str = str
}
Foo.prototype.handle = function(e) {
  alert(this.str)
}
Foo.prototype.init = function() {
  var _this = this
  this.listener = function(e){
    _this.handle(e, this)
  }
  this.element.addEventListener('click', this.listener)
}
Foo.prototype.cancel = function() {
  this.element.removeEventListener('click', this.listener)
}

var foo = new Foo(document, "hello")
foo.init()
foo.cancel()

// やっぱり書き換え可能
Foo.prototype.handle = function(){}
foo.handle = function(){}

いちいち新しい関数を作るので効率は悪い。継承の利点の一つが失われている。
その点を除けばhandleEventと同等のことができる。

bind + コンストラクタ

Foo.prototype.init = function() {
  this.listener = this.handle.bind(this)
  this.element.addEventListener('click', this.listener)
}

後からイベントリスナのcallbackを書き換えることは不可能。でもremoveはできる。
(でも書き換えなんて実際するかどうか)

ということで、handleEventでしかできないことがあるのか考えたけど思いつかなかった。他の方法でわざわざ遠回りすることもないので、オブジェクトなら素直に使うということで。

ちら裏

(´-`).。oO( 最初MDN見たときに何かわからなかった。)
当時は変数名の最初が大文字ならコンストラクターを意味していると知らなかったので、記載されている例が理解できなかった。