当 Proxy 照进现实
2022年7月16日 · 200 字 · 1 分钟 · ES6 Proxy Object Method Defination This Reactive Arrow Function
前提
在 ES5 及以前,我们为对象定义一个方法的时候,需要这样:
const person = {
bark: function(){}
}
在 ES6 之后,JS 允许写这样的代码:
(消除了冒号和 function 关键字,并且,这种简写方法可以使用 super 关键字来修饰)
const person = {
bark(){}
}
实践
目前在做一个 workflow 的编辑面板,其中每个 node 都是可拖拽的。
于是需要在更新坐标数据的时候,同时更新这个 node 的 UI 位置。
export default class Node{
constructor(el){
this.el = el;
this.curPos = {x:0,y:0};
}
moveTo(x,y){
this.curPos.x = x;
this.curPos.y = y;
this.el.style.left = x + 'px';
this.el.style.top = y + 'px';
}
}
于是,一个想法自然而然就会产生:坐标数据的更新和 UI 的更新几乎总是同步的,为什么不根据数据的变更去自动更新 UI 位置呢,所以第一次尝试使用 proxy。(虽然目前的开发框架整体是响应式的,但绘制画布这一块还是手动去维护逻辑的。手动维护在我看来还是比较有必要的)
export default class Node{
constructor(el){
this.el = el;
this.curPos = new Proxy(
// target
{x:0,y:0},
// handler
{
get(target, prop){
return target[prop];
},
set(target, prop, val){
const which = prop === 'x' ? 'left' : prop === 'y' ? 'top' : '';
if(which){
target[prop] = val;
this.el.style[which] = val + 'px';
return true;
}else{
// assert - incorrect prop, throw error or failed with silence
return false;
}
}
});
}
moveTo(x,y){
this.curPos.x = x;
this.curPos.y = y;
}
}
看起来多此一举,代码并没有减少多少,甚至看上去更乱了。 但谁知道呢,有两个原因鼓励我继续探索:
- 想用一下 proxy,至少也要浅尝辄止
- 这里的例子只是最小复现,未来很可能会有 moveXTo, moveYTo, moveXBy, moveYBy 等方法都可以触发此 proxy handler
问题及总结
可是上面的代码是有问题的: proxy 的 setter 里的 this 指向了 这个 proxy 的 handler, 我几乎没有用过 bind 去改变 this 的指向,现在该不会要 bind 吧,多么古旧的做法。 一定是什么出了问题,莫非这是 proxy 的特异(exotic)行为?
经过两个 console.log 的排查,确认了这个 setter 不是一个箭头函数,而是一个普通的匿名函数。 改成箭头函数之后,this 就能拿到正确结果了。
直觉欺骗了我。
后续: 几个月前借着重构 UI 的时间,把这坨屎铲了。——2023年7月3日。