简体   繁体   中英

this variable undefined when passed into callback function

I am trying to take an OOP approach in javascript by following this and the one on the mozilla website . When I instantiate my function/class with the new keyword the graph renders correctly, however when a this variable is passed into a callback function it becomes undefined , I was wondering if there was a way around this?

Here is my code:

function lineBasedCharts (renderTo, chartType, deviceID, metricType, refreshCycle, title, subtitle, yAxis, tooltip) {
    this.mRenderTo = renderTo
    this.mRefreshCycle = refreshCycle
    this.mChartType = chartType
    this.mTitle = title
    this.mSubtitle = subtitle
    this.mYAxis = yAxis
    this.mTooltip = tooltip

    ...

    this.chart = new Highcharts.Chart(options, function (ch) {
        //use a callback function off the end of highcharts, for when the chart has fully loaded.
        AddSeries(ch, deviceID, metricType);

        if (ch.series[0].data.length > 0) {
            setTimeout(requestData, this.mRefreshCycle, ch, ch.series[0].data[ch.series[0].data.length - 1].x, deviceID, metricType, this.mRefreshCycle);
        }
    });
}

and this is how I instantiate my object

var chart = []
chart.push(new lineBasedCharts('lineChart', 'spline', 49, 'TEMP', 30000, 'temp', 'Temp in degrees', 'Temperature (°C)', '°C'))

this.mRefreshCycle seems to be become undefined when used in the callback function.

Pass refreshCycle as argument to setTimeout instead. It is always dangerous to use this since it changes meaning dependent upon context.

this is most likely referring to your created Highcharts.chart object. Create an alias of this (_this = this) and try use _this.mRefreshCycle

function lineBasedCharts (renderTo, chartType, deviceID, metricType, refreshCycle, title, subtitle, yAxis, tooltip) {
    //Add alias for this
    _this = this;
    this.mRenderTo = renderTo
    this.mRefreshCycle = refreshCycle
    this.mChartType = chartType
    this.mTitle = title
    this.mSubtitle = subtitle
    this.mYAxis = yAxis
    this.mTooltip = tooltip

    ...

    this.chart = new Highcharts.Chart(options, function (ch) {
        //use a callback function off the end of highcharts, for when the chart has fully loaded.
        AddSeries(ch, deviceID, metricType);

        if (ch.series[0].data.length > 0) {
            //change how you refer to mRefreshCycle
            setTimeout(requestData, _this.mRefreshCycle, ch, ch.series[0].data[ch.series[0].data.length - 1].x, deviceID, metricType, this.mRefreshCycle);
        }
    });
}

The scope of 'this' within the callback function is likely the "new Highcharts.Chart", not the "lineBasedChart" function. It has no idea what mRefreshCycle is because it does not exist within the scope of the newly created object. You might be able to just remove the 'this' and take advantage of the closure mechanism.

Also, the "options" being passed to the "new Highcharts.Chart()" are undefined.

Sending this from my phone. I apologize for lack of markdown. I will update the format of answer when I get a chance.

As others have suggested, I would remove the "this" identifier and use the parameter that is being passed in. They're the same value at this point anyway, as long as you haven't changed the value of mRefreshCycle at a later time. If you have a mutator that does this then you will need to tweak this slightly.

How about making lineBasedChart an actual object?

var LineBasedChart = function (renderTo, chartType, deviceID, metricType, refreshCycle, title, subtitle, yAxis, tooltip) {
    var self = this;
    this.mRenderTo = renderTo
    this.mRefreshCycle = refreshCycle
    this.mChartType = chartType
    this.mTitle = title
    this.mSubtitle = subtitle
    this.mYAxis = yAxis
    this.mTooltip = tooltip

    ...

    this.chart = new Highcharts.Chart(options, function (ch) {
        //use a callback function off the end of highcharts, for when the chart has fully loaded.
        AddSeries(ch, deviceID, metricType);

        if (ch.series[0].data.length > 0) {
            setTimeout(requestData, self.mRefreshCycle, ch, ch.series[0].data[ch.series[0].data.length - 1].x, deviceID, metricType, self.mRefreshCycle);
        }
    });

    /*Mutator to update refresh cycle value*/
    this.setRefreshCycle = function(refreshCycle) {
        this.mRefreshCycle = refreshCycle;
    }
}

This would be useful if you're creating multiple objects eg

var lineBasedChart1 = new LineBasedChart(...)
var lineBasedChart2 = new LineBasedChart(...)

You don't necessarily need to use a mutator you could just call on the property directly.

lineBasedChart1.mRefreshCycle = 500;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM