一个很简单的事情,给页面上的画布元素增加一个快捷键。我刚开始大致是这么实现的,前提这已经是在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。
这就引申出了两个问题:
- 输出结果不对,意味着
this的指向肯定不对,如何修改this指向? - 直接做一个
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指向,即call,apply和bind。那......用谁呢?
这就涉及到了三者的区别。
用一行等式其实可以理解这三个关键词的用法。
catMethod.call(dog, a, b)
= catMethod.apply(dog, [a, b])
= (catMethod.bind(dog, a, b))()
这三者,除了括号内参数的不同,还有一个重大区别:
call、apply回传function的执行结果bind被调用后返回的是调用函数的指针,即绑定this后的一个新函数。
上面的需求该怎么解决
答案是使用bind。
因为我们要绑定的是一个监听事件,当事件被监听到时,才需要执行回调函数!!!而非像call、apply一样直接执行了绑定函数。
那么代码就要改成
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);
这样,最后就解决了问题,满足了产品经理。。。