3

背景

最近做项目时需要为折线图(angular 中使用 echarts 画的图)上的点设置更加个性化的显示和操作:

  1. 不同数值区间的点以不同颜色显示。
  2. 点击图上的点后会执行自定义的操作。

查看官方文档后发现,这其实并不困难。因为 echatrs 支持以方法的返回值作为图表中某个属性的值,也提供了接口以监测点击事件,可以在其回调函数中设置点击后的自定义操作。

echarts 官方文档点击这里

于是对于点的颜色显示问题就有了这样的代码:

  inputWarningValue: number = 1000;

  lineChartOption = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [{
      data: [820, 932, 901, 934, 1290, 1330, 1320],
      type: 'line',
      itemStyle: {
        normal: {
          color: function (params: { value: any[]; }) {
            const yValue = params.value[1];
            if (yValue > this.inputWarningValue) {
              return '#ff0031';  // 红色
            } else {
              return '#048ABF';  // 蓝色
            }
          }
        }
      },
      lineStyle: {color: '#0367A6'},
    }]
  } as EChartsOption;

这么写会报错:
image.png

但是将函数的写法由 function () {} 改为箭头函数 () => {} 就不会报错了。像这样:

...
itemStyle: {
        normal: {
          color: (params: { value: any[]; }) => {
            const yValue = params.value[1];
            if (yValue > this.inputWarningValue) {
              return '#ff0031'; // 红色
            } else {
              return '#048ABF'; // 蓝色
            }
          }
        }
      },
...

对于点击后的自定义操作我是这么写的:

  constructor(private router: Router,
              private route: ActivatedRoute) {

  }
  ngOnInit() {
    this.initCharts();
  }
  initCharts() {
    const ec = echarts as any;
    const lineChart = ec.init(document.getElementById('lineChart'));
    lineChart.on('click', function () {
      console.log('item click');
      // 点击某个点后跳转路由
      this.router.navigate(['any-url'], {relativeTo: this.route}).then();
    })
    lineChart.setOption(this.lineChartOption);
  }

这样也会报错:
image.png

后来尝试发现,除了改变函数的写法外,也还可以用注入静态变量的方式解决的这个问题:

  private static routerRef: Router
  private static routeRef: ActivatedRoute
  constructor(private router: Router,
              private route: ActivatedRoute) {
    Dec1209Component.routerRef = router;
    Dec1209Component.routeRef = route;
  }
  ngOnInit() {
    this.initCharts();
  }
  initCharts() {
    const ec = echarts as any;
    const lineChart = ec.init(document.getElementById('lineChart'));
    lineChart.on('click', function () {
      console.log('item click');
      // 点击某个点后跳转路由
      Dec1209Component.routerRef.navigate(['any-url'], {relativeTo: Dec1209Component.routeRef}).then();
    })
    lineChart.setOption(this.lineChartOption);
  }

分析之后可以发现,上述两个情形,本质上其实是相同的问题:在不同情况下,this 的指向其实是不一样的。

不同情况下 this 的指向

结果集与情况分类

首先,我们对于 this 指向的结果集需要有个大致的概念。
image.png

我们一般接触的 this 指向有两个可能的结果:当前对象当前对象的上下文

其次我们都知道,this 的使用场景通常都在某个函数中,所以这里所说的不同情况其实指的也就是函数的不同情况。

函数在写法上我们最常见有两种:

  1. 普通写法:function() {} 。一般在类中我们都会这么定义函数。
  2. 箭头函数:() => {} 。一般在回调中我们会用这种写法。

函数的调用方式也可以分类:

  1. 对象方法调用:object.function() 。最常见的调用方式。
  2. 将方法赋值给变量后再调用:const func = object.function; func()。不太常见。

我们姑且可以根据上述分类来分别测试 this 的指向。

测试及结果

angular 中我们可以在某个组件中准备这样的测试代码:

export class Dec1209Component implements OnInit {
  value: string = 'getFromContext';
  ngOnInit() {
    const object = {
      value: "getFromSelfObj",
      getValueByArrow: () => {
        return this.value;
      },
      getValueByGeneral() {
        return this.value;
      },
    };

    console.log('箭头函数 + 对象方法调用', object.getValueByArrow());

    const arrowFunc = object.getValueByArrow;
    console.log('箭头函数 + 赋值变量调用', arrowFunc());

    console.log('普通写法 + 对象方法调用', object.getValueByGeneral());

    const generalFunc = object.getValueByGeneral;
    console.log('普通写法 + 赋值变量调用', generalFunc());
  }
}

运行后得到以下结果:
image.png

总结

一、函数写法为箭头函数 function: () => {}

不管是对象方法调用,还是赋值变量调用,this 指向当前定义函数时所在的上下文。

二、函数写法为普通写法 function() {}

1.对象方法调用时,this 指向的就是当前执行函数时所在的上下文,通常就是当前对象。
2.赋值变量调用时,this 似乎既不指向当前对象,也不指向当前对象的上下文。(待深究)

希望这篇文章对你有帮助。

参考资料

https://zhuanlan.zhihu.com/p/104565681

https://zhongsp.gitbooks.io/typescript-handbook/content/doc/w...


HHepan
164 声望13 粉丝

河北工业大学