【问题标题】:D3 callback function's `this` shadows object's `this`D3 回调函数的 `this` 遮蔽了对象的 `this`
【发布时间】:2019-05-19 05:30:18
【问题描述】:

我有一个包装 D3 的类。我正在将其转换为 TypeScript,我收到以下两个错误并且无法弄清楚如何修复它们(实际上有多个错误,但它们都与这对相似),

src/d3-graph.ts:295:19 - error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. 
295         d3.select(this)
                      ~~~~

  src/d3-graph.ts:294:23
    294       .on('mouseout', function(d: any) {
                              ~~~~~~~~
    An outer value of 'this' is shadowed by this container.

代码(类中的方法,添加了行号以供参考),

...
1 private _enableNodeHighlightOnHover() {
2   this._nodes
3     .on('mouseout', function(d: any) {
4       d3.select(this)
5         .style('stroke-width', '2px')
6     })
7 }
...

注意第 2 行,this 指的是类的实例对象。

在第 4 行,this 指的是对象 D3 已绑定到提供给 on 的回调(第 3 行)。还要注意使用function 而非(...) => { ... }——以允许D3 将this 绑定到它需要的对象。

如果我可以通过其他方式访问我在d3.select(this) 中使用的 D3 对象,我将非常高兴在回调函数中失去 this 的使用。但我不确定那会是什么。

这种模式还有其他用途需要适应,

private _enableDrag() {
  const that = this

  this._drag = d3.drag()
  this._drag
    .on('drag', function(d: any) {
      d.x += d3.event.dx
      d.y += d3.event.dy

      d3.select(this)
        .attr('cx', d.x)
        .attr('cy', d.y)

      that._links.each(function(l: any) {
        if (l.source === d.id)
          d3.select(this)
            .attr('x1', d.x)
            .attr('y1', d.y)
        else if (l.target === d.id)
          d3.select(this)
            .attr('x2', d.x)
            .attr('y2', d.y)
      })

      if (that._nodeTextLabels === null)
        logger.warn(
          'enableDrag called before this._nodeTextLabels has been initialized')
      else
        that._nodeTextLabels.each(function(n: any) {
          if (n.id == d.id)
            d3.select(this)
              .attr('x', d.x + D3Graph._LABEL_FONT_SIZE / 2)
              .attr('y', d.y + 15)
        })

      that._nodes.each(function(n: any) {
        if (n.id == d.id)
          d3.select(this).select('circle')
            .attr('cx', d.x)
            .attr('cy', d.y)
      })
    })
}

【问题讨论】:

  • _links_nodes 的类型是什么?如果您的类型是正确的(您的和库定义都是正确的),那么简单函数中的this 应该是正确的类型。您可以为this (function (this: any, other params)) 添加显式注释,但我建议您调试为什么编译器无法确定this 是谁
  • @TitianCernicova-Dragomir _links 和 _nodes 分别是d3.select(document.body).select('.node').select('.link') 返回的值。其中那些分别指的是带有“.nodes”和“.link”类的html元素。
  • 如果我在类中的 this._nodes 和 _links 声明中指定一个“选择”类型,我会遇到其他错误,例如选择类型上不存在selectAll。我使用 Selection 是因为它看起来像它返回的内容,但我可能弄错了 (github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/d3/…)
  • 作为this 的替代方案,您可以使用侦听器函数的第二个和第三个参数:function(d,i,g) { d3.select(g[i])...,相当于function() { d3.select(this)...。 (g 是选择中的元素 - 请参阅 selection.on 下的文档 here
  • @AndrewReid 这就是我真正想要的。当我阅读文档时,我一定忽略了文档中的那一点细节。谢谢。让我试一试。

标签: javascript typescript d3.js


【解决方案1】:

作为this 的替代方法,尝试使用监听器函数的第二个和第三个参数:

  function(d,i,group) { d3.select(group[i] ... })

这是因为当使用selection.on("event",function(){})selection.each(function() {}) 时,d3 将当前元素绑定到this 以获取所提供的函数。 D3 还将三个参数绑定到提供的函数:

  • 当前数据 (d)
  • 当前索引 (i)
  • 选择中的元素组,而不是选择的元素(这里命名为group

使用时:

selection.on("event", function() { d3.select(this); })

你实际上是在做同样的事情:

selection.on("event", function(d,i,group) { d3.select(group[i]); })

因为group 保存选择的元素,i 是当前索引,group[i] 是当前元素,与this 相同。

使用group[i] 允许使用箭头函数和环境,这可能会改变this 的上下文,同时仍访问当前元素。

【讨论】:

    【解决方案2】:

    在使用eachmap等的cheerio上也会出现这个错误。

    TypeScript(>=2.0) 可以指定函数的这个类型。以下代码可以避免 TS2683 错误。 https://www.typescriptlang.org/docs/handbook/functions.html#this-parameters

    .on('mouseout', function(this: any, d: any) {
    
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-16
      • 1970-01-01
      • 1970-01-01
      • 2012-09-11
      • 2014-07-31
      • 1970-01-01
      • 2015-07-27
      • 2015-02-17
      相关资源
      最近更新 更多