简体   繁体   中英

JavaScript closure can't access outer variables

I have such a code and don't know why in anonymous function passed to nv.addGraph() I can't access variables from outer function like a , zcls or this.model .

function (out) {
// Here you call the "this" means the widget instance. (@see Mylable.js)
var zcls = this.getZclass(),
uuid = this.uuid;

// The this.domAttrs_() means it will prepare some dom attributes,
// like the pseudo code below
/*
 * class="${zcls} ${this.getSclass()}" id="${uuid}"
 */
var a = this.domAttrs_();
out.push('<span ', this.domAttrs_(), '>fds</span><div id="chart"><svg></svg></div>');



nv.addGraph(function() {
    var chart = nv.models.multiBarChart()
    .transitionDuration(350)
    .reduceXTicks(true)   // If 'false', every single x-axis tick label
    // will be rendered.
    .rotateLabels(0)      // Angle to rotate x-axis labels.
    .showControls(true)   // Allow user to switch between 'Grouped' and
    // 'Stacked' mode.
    .groupSpacing(0.1)    // Distance between each group of bars.
    ;

    chart.xAxis.tickFormat(d3.format(',f'));

    chart.yAxis.tickFormat(d3.format(',.1f'));

    var data = [{
        key: 'Some key',
        color: '#ff44ee',
        values: [{
            x: 1,
            y: 3
        }, {
            x: 3,
            y: 4
        }]
    }]

    d3.select('#chart svg').datum(data).call(chart);
//      d3.select('#chart svg').datum(this.model.data).call(chart);

    var someData = this.model.data;

    nv.utils.windowResize(chart.update);
    return chart;
});

This function is in some way used in ZKoss Widget so in this function it's possible to access its properties like this.model, but it's not possible in inner anonymous function. I have no idea what's wrong with it, I just started coding in JS.

You don't access a or zcls in your closure but you should have no problem doing so.

As for this , it is always a function local reference that is set by the caller of the function. If you want access to this as viewed by the parent function you need to either copy it to a local variable such as,

var that = this;

in the parent and then refer to that instead of this in the closure, or, alternately, you can call bind() on the function passing in this such as.

nv.addGraph(function () {
   ...
}.bind(this));

which will cause the this in the function to be the same value as the parent's this .

The this keyword in JavaScript doesn't work the same as in other OO languages for example Java . It's value is determined during run-time and it depends exclusively on the way we invoke a function.

There are four different ways that a function can be called:

  1. Constructor call: Using the new keyword; This refers to the newly created instance

    function Car = function(){ //... } var car = new Car();

  2. Function call: When invoking a function that is defined in some scope eg global scope.In this case, this refers to the global object

    function someFunc(){} someFunc();

  3. Method call: When invoking a function that is defined as a member of an object. eg

    var obj = { func: function(){} } obj.func();

In this case this points to the object itself.

4 Call/Apply: Finally a function can be called with the help of two methods that are defined on the prototype of the Function constructor. In this case we are free to set the value of this ourselves; var obj = { func: function(){} } function someFunc(){}

someFunc.call(obj);

In your case, in order to access this.model you need to explicitly define where you want the this keyword to point.

You have to options:

  1. Define a local variable eg var that = this;
  2. Use the ES5 bind method

    nv.addGraph(function () { ... }.bind(this));

An inner anonymous function will have the same scope as it's containing function.

You may consider passing-in zcls , or defining it within the function you are passing to nv.addGraph

MDN explains this really well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope

You could define the function argument, then pass it by name. For example:

var fcnArg = function() { ... }

nv.addGraph(fcnArg);

Another good discussion on this available here: Passing a function as an argument in a javascript function

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