使用selection.call()时如何更改"this"上下文?附D3可复用图例插件代码
调整D3 selection.call()中的this上下文
好问题!在使用Mike Bostock的可复用图表模式时,想要修改selection.call()调用时draw函数内部的this上下文是个很常见的需求。先明确一下默认行为:当你调用selection.call(legend())时,draw函数里的this默认会绑定到当前的D3 selection对象——这是selection.call()的内置规则。
下面给你三种可行的解决方案,按推荐程度排序:
方案一:给图例添加可配置的上下文参数(最贴合复用模式)
既然你遵循的是可复用图表的设计思路,最好把上下文也做成和width、height一样的可配置项,保持API的一致性。修改你的代码如下:
function legend () { var width = null, height = null, context = null; // 新增context变量 // 新增context的getter-setter方法 draw.context = function (val) { if (!arguments.length) { return context; } context = val; return draw; }; draw.width = function (val) { if (!arguments.length) { return width; } width = val; return draw; }; draw.height = function (val) { if (!arguments.length) { return height; } height = val; return draw; }; function draw (selection) { // 优先使用配置的context,未配置则用默认的this(即selection) const ctx = context || this; // 后续逻辑用ctx代替this即可 console.log(ctx); // 输出你配置的上下文对象或selection // 你的绘制逻辑... } return draw; }
使用时直接通过链式调用配置上下文:
// 自定义你的上下文对象 const myCustomContext = { legendColor: "#333", fontSize: 14 }; // 调用时配置上下文 selection.call( legend() .width(200) .height(50) .context(myCustomContext) );
方案二:用bind()手动绑定上下文
如果你只是临时需要切换一次上下文,可以在调用selection.call()前,用Function.prototype.bind()把draw函数的上下文绑定到目标对象。注意要先完成width、height的配置,再调用bind()(因为bind()会返回一个新函数,原函数的getter-setter不会继承到新函数上):
const myContext = { foo: "bar" }; selection.call( legend() .width(200) .height(50) .bind(myContext) // 绑定上下文到myContext ); // 此时draw函数内的this就是myContext function draw(selection) { console.log(this); // 输出myContext // 你的绘制逻辑... }
方案三:在draw内部用call()/apply()切换局部上下文
如果只是draw函数中的某一段逻辑需要特定上下文,没必要修改整个函数的this,可以用call()或apply()包裹那段逻辑:
function draw(selection) { // 这里的this还是默认的selection console.log(this); // 输出selection // 定义需要特定上下文的逻辑 function renderLegendItems() { console.log(this); // 输出myContext // 渲染图例项的逻辑... } // 调用时切换上下文 renderLegendItems.call(myContext, selection); }
总结
最推荐方案一,它完美契合可复用图表的设计理念,让你的图例组件API更统一、更灵活。
内容的提问来源于stack exchange,提问作者1Cr18Ni9
相关产品推荐
相关产品推荐

