基于JavaScript Proxy实现标量数据与HTML元素属性的优雅自动绑定方案问询
先唠一句,我这可能有点XY问题的味儿,但纯粹是出于理论研究的兴趣,不是有啥急着要解决的实际需求。
我的最终目标是搞一个轻量框架,能把标量数据绑定到HTML元素的某个属性上,让属性能在值变化时自动更新。
我一直在琢磨怎么搞出语法最优雅的实现,把IDE代码补全和开发者的使用便利性放在第一位。
我知道Proxy这玩意儿,后面也会提到它。我的核心难题其实是想搞点语法糖,所以各位不妨跟着我的思路走——实在不行我也可以退而求其次用那些成熟方案,但还是想整个更“丝滑”的写法。
先假设我已经有个不错的小框架,能给DOM节点加额外功能,必要时还能给它套个Proxy。
我心目中最理想的语法/API是这样的:
let someVar = "foo" let someElement = MyFramework.getById("some-div"); // 返回带额外方法的代理HTMLElement someElement.bind("innerText", someVar); // 之后... someVar = "bar"; // someElement.innerText 自动更新
先别急着吐槽,我知道这根本行不通,因为原始类型(Primitive)没法被Proxy,所以我也接受把数据包在一个可被Proxy的state对象里:
let state = MyFramework.createState(); // 返回State对象的代理 state.someVar = "foo"; let someElement = MyFramework.getById("some-div"); someElement.bind("innerText", state.someVar);
问题就出在这儿:我在.bind()函数里实际需要的是state对象里的"someVar"这个属性名,但现在拿到的只是state.someVar的值。
我试过在createState()里加个Proxy,让getter陷阱返回属性名而非值,代码大概是这样:
MyFramework.createState = function(initialProps = {}){ let proxy = new Proxy(initialProps, { get(target, prop, receiver){ return prop; } }); return proxy; }
但这么一来我就没法正常用这个变量了,因为我不知道怎么让这个get陷阱有条件触发——也就是只有在.bind()函数里访问时才返回属性名,其他情况就正常返回值(比如用Reflect(...arguments))。
我特别想避免下面这种写法:
someElement.bind("innerText", state, "someVar");
类似的,下面这种写法也显得重复,还容易让开发者写错:
someElement.bind("innerText", state, state.someVar)
另外我也考虑过这种写法:
state.someVar.bind(someElement, "innerText");
但想了想这也行不通,因为someVar还是个标量(原始类型),没法给它加自定义的.bind()方法。
我还琢磨过能不能拦截第一次访问state.someVar的时候,给这个值也套个Proxy,然后用get陷阱来处理不存在的"bind"函数,但同样的问题——没法做到上下文判断,这样开发者就没法在普通场景下直接用state.someVar当标量用,最后又得搞出state.someVar.set("new val")和state.someVar.get()这种难看的写法,那我这探究就没啥意义了。
我感觉自己对JS内部机制的了解已经挖得差不多了,是不是有啥明显的点被我漏掉了?
最后说一句,如果有人想说“你这是在纠结Y,但其实应该换个思路搞X”,我也感激,但前提是真的没有办法实现我想要的优雅语法。就像我之前说的,成熟方案有不少,但我就是想整个更“酷炫”的写法。
备注:内容来源于stack exchange,提问作者Tom Auger

