call、apply和bind在真正需求里的学以致用

82 阅读2分钟

一个很简单的事情,给页面上的画布元素增加一个快捷键。我刚开始大致是这么实现的,前提这已经是在vue的一个对象的作用域下。

this.attr = true;
const canvasContainer = this.stage.container();
canvasContainer.addEventListener('keydown', this.keydownMethod);

keydownMethod(e) {
   if(e.keyCode == 69) { //按“E”键
     console.log(this.attr);
   }
}

可是这样的输出结果却是undefined,而非预料中的true

这就引申出了两个问题:

  1. 输出结果不对,意味着this的指向肯定不对,如何修改this指向?
  2. 直接做一个keydownMethod.bind(this)是否真的能达到我要的效果?

逐个解答。

如何修改this指向

此时this正指向了谁

keydownMethod方法中打印值,可以得到this指向的结果是canvasContainer对应的DOM元素。

keydownMethod(e) {
   if(e.keyCode == 69) { //按“E”键
      console.log(this);//<div id="imageCanvas" draggable="true" >…</div>
      console.log(this.attr);//undefined
   }
}

[ call , apply ] vs [ bind ]

当然,我们有最简单的abc方法去修改this指向,即callapplybind。那......用谁呢?

这就涉及到了三者的区别。

用一行等式其实可以理解这三个关键词的用法。

catMethod.call(dog, a, b)
= catMethod.apply(dog, [a, b])
= (catMethod.bind(dog, a, b))()

这三者,除了括号内参数的不同,还有一个重大区别

  • callapply回传function的执行结果
  • bind被调用后返回的是调用函数的指针,即绑定this后的一个新函数。

上面的需求该怎么解决

答案是使用bind

因为我们要绑定的是一个监听事件,当事件被监听到时,才需要执行回调函数!!!而非像callapply一样直接执行了绑定函数。

那么代码就要改成

this.attr = true;
const canvasContainer = this.stage.container();
canvasContainer.addEventListener('keydown', this.keydownMethod.bind(this));

keydownMethod(e) {
   if(e.keyCode == 69) { //按“E”键
     console.log(this.attr);
   }
}

修改后,确实能正常打印attr值了。但是回到需求的完整性上来,给键盘事件增加了监听,是不是还要在适当的时机取消监听呢?

直接写成keydownMethod.bind(this)真的对吗?

如果你说直接removeEventListener那不是很简单吗?至于你还在这里提一嘴?

。。。

我在移除舞台元素监听事件的时候,确实直接这么写了。

const canvasContainer = this.stage.container();
canvasContainer.addEventListener('keydown', this.handleStageKeydown);
//也尝试了canvasContainer.removeEventListener('keydown', this.handleStageKeydown.bind(this));

但是发现根本没有移除掉!!!

原因其实刚才已经说了,bind回传的是绑定this后的一个新函数。

fn.bind(aa) !== fn.bind(bb)

那应该怎么办呢?

那就把bind函数提前保存起来得了。。。

this.keydownMethod = this.keydownMethod.bind(this);
canvasContainer.addEventListener('keydown', this.keydownMethod);
...
...
...
canvasContainer.removeEventListener('keydown', this.keydownMethod);

这样,最后就解决了问题,满足了产品经理。。。