0

I coded a class in JavaScript, and I'm trying to modify one of my public properties inside a private function.

Here is an example of my code:

MyClass = function(callback){

    this.tabs = [];
    var tabs_callback = function(){};

    this.setTabsCallback = function(callback) {
        tabs_callback = callback;
    }

    var _doStuff = function(callback) {

        // doing stuff

        this.tabs = [1, 2, 3];

        console.log("Inside doStuff");
        console.log(this.tabs);

        tabs_callback();
    }

    this.doStuff = function() {
        _doStuff();
    }
}


var myObject = new MyClass();

myObject.setTabsCallback(function () {

    console.log("Inside callback");
    console.log(myObject.tabs);
});

myObject.doStuff();

And here is what I get in the console:

Inside doStuff
[ 1, 2, 3 ]
Inside callback
[]

Why am I not able to see my modification from the callback function?

B 7
  • 670
  • 7
  • 23
  • 2
    Probably the scope of `this`. Up at the top of the `MyClass` function (perhaps the line above `this.tabs = [];`) create a variable `var self = this;` and use `self` in place of `this` where you want to access the public properties. – jonhopkins Sep 27 '16 at 14:23
  • Oh great! You're right, it worked! Thank you very much – B 7 Sep 27 '16 at 14:33

4 Answers4

1

See this keyword. Using arrow functions is a good way to prevent many of these issues:

var _doStuff = (callback) => { ...

Yet you still need to be aware of it.

ASDFGerte
  • 4,695
  • 6
  • 16
  • 33
1

By default, JS does not use lexical context binding, and when you call ordinary functions, this defaults to window (or null if you are in strict mode).

There are plenty of ways to avoid it: you can save this to variable, as mentioned above. It will work in 100% of cases.

Also you can explicitly bind _doStuff to context, using Function#bind method: do something like _doStuff = _doStuff.bind(this) after it's initialization or you may call it like _doStuff.bind(this)(). IEs prior to IE9 (and, maybe, some other browsers, I'm not quite sure) don't support bind.

In the end, you can use arrow function, as it also mentioned above: they use lexical binding and this will be same as you expect. Arrow functions available since ES6, but, I suppose, if you are making constructors like this, it's not ES6.

ixth
  • 78
  • 7
1

When _doStuf is invoked through a function like

this.doStuff = function() {
                 _doStuff();
               }

_doStuff is bound to the global object. Hence you should either bind it's definition to the this or use .call(this) when invoking _doStuff function. So modifying your code as follows will help;

MyClass = function(callback){

    this.tabs = [];
    var tabs_callback = function(){};

    this.setTabsCallback = function(callback) {
        tabs_callback = callback;
    }

    var _doStuff = function(callback) {

        // doing stuff

        this.tabs = [1, 2, 3];

        console.log("Inside doStuff");
        console.log(this.tabs);

        tabs_callback();
    } // <<< you can either use .bind(this) here 

    this.doStuff = function() {
        _doStuff.call(this);  // <<< or use .call(this) here to bind _doStuff to the this
    }
}


var myObject = new MyClass();

myObject.setTabsCallback(function () {

    console.log("Inside callback");
    console.log(myObject.tabs);
});

myObject.doStuff();
Redu
  • 25,060
  • 6
  • 56
  • 76
1

Because _doStuff is defined as a function, it has its own this context, which is why you cannot access the this.tabs that you defined outside of the private function.

There are two simple ways to resolve this.

First, you can simply create a private variable which holds a reference to this outside the function:

...
var that = this;
var _doStuff = function(callback) {
    // inside _doStuff, use "that" instead of "this"
...

Or, second, you could instead use an arrow function. Arrow functions are a relatively new addition to Javascript. They do not create a separate context with its own this reference, so when you use the this object it will still refer to the same object as outside the function, so your function will work correctly:

....
var _doStuff = (callback) => {
    // now _doStuff doesn't create a new "this" object, so you can still access the one from outside
...
Steve
  • 146
  • 3