David K.
David K.

Reputation: 729

ES6 Class Variables Out of Scope?

Just made the switch over to ES6, running io.js.

I'm writing some class code, but I'm having an unexpected error.

'use strict';

var _ = require('lodash');

class Country {

  constructor(blocked) {
    this.blocked = ['USA'];
  }

  ok(input) {
    console.log('Receiving...',input['country']);
    console.log('Blocked:', this.blocked);
    if(_.includes('USA', input['country'])) {
      return -1;
    }
    return 0;
  }

}

module.exports = Country;

For whatever reason, everything works well except for the this.blocked class variable.

When I console log it, it shows up as Blocked: undefined.

Any thoughts as to what's going on here?

Addition

I'm calling the function in another class as follows...

var Country  = require('./filters/country.js');
var country  = new Country();

class FilterClassifier {

    constructor() {
      var self = this;
      self.filters = [country.ok];
    }

    userFilter(params) {

      var self = this;

      var input = {
        country   : params.country,
      };

      console.log(self.filters[0](input));
    }

}

module.exports = FilterClassifier;

Upvotes: 2

Views: 1810

Answers (2)

Dylan Valade
Dylan Valade

Reputation: 5605

It looks like this.blocked is scoped to the constructor function, this in ok doesn't have a identifier called blocked.

Edit: I was wrong, thanks Pointy. In the interest of helping others who stumble on this post I'm not going to delete my wrong answer but instead share what I learned. David, in the comments you asked for a way to fix it. this could be replaced with let variables to avoid confusion or use this with bind() when a function is called.

class Country {

  // variable scoped to class with let
  // set blocked = value in constructor
  // should not need 'this.' nor 'bind()'

  let blocked = [];

... 
}

"Determining this" says you had default this binding: undefined. You hoped for implicit binding and got explicit binding to work with country.ok.bind(country).

Further reading illustrating how the fat arrow => and var self = this; make this confusing.

Upvotes: 0

loganfsmyth
loganfsmyth

Reputation: 161647

As mentioned in the comments, the way you are calling the function removed the context of the function.

self.filters = [country.ok];

and then

console.log(self.filters[0](input));

means that this inside ok will not be country. You'll need to do

self.filters = [country.ok.bind(country)];

or

self.filters = [() => country.ok()];

I'd recommend reading up on this in javascript. The short answer in this particular case is that this is defined based on how a function is called.

var a = {};
a.fn = function(){};

var b = {};
b.fn = a.fn;

b.fn();

When calling b.fn(), this inside the function is b. This is because when calling a function using the form foo.bar(), this inside a function is defined as the object that the function is called on (foo). In your case, you have

self.filters[0]();

that means that this inside your ok functions is actually self.filters for the same reason.

If you have a specific this that matters, it is your responsibility to make sure that as you pass around a function, that the function you are passing will set the proper this. Using fn.bind(foo) will return a new function which, when called, call fn with a given this.

Upvotes: 4

Related Questions