0

Given following JavaScript code:

({
  foo: 1,
  bar: 2,
  zoo: 3,
  test: function(i) {
    var g = function(i) {
        alert(i + zoo);
    }
    g(i);
  }
}).test(2);

Why is zoo undefined in alert()? Which kind of syntax can I use to correctly reference to zoo and get alert display for value 5?

Update: I would prefer solution where only implementation of g needs to change, if at all possible.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112

4 Answers4

3

Use an arrow function to preserve the "outside" value of this, and use it to access the value of zoo property:

({
  foo: 1,
  bar: 2,
  zoo: 3,
  test: function(i) {
    var g = i => alert(i + this.zoo);
    g(i);
  }
}).test(2);
  • What is the real world compatibility of this syntax? I'm mainly interested in web browsers. – Mikko Rantalainen Apr 08 '16 at 07:32
  • 1
    @MikkoRantalainen you can check the compatibility at [https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Browser_compatibility](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Browser_compatibility) and the popularity of browsers at [http://www.w3schools.com/browsers/browsers_stats.asp](http://www.w3schools.com/browsers/browsers_stats.asp) – hargasinski Apr 08 '16 at 07:39
  • I would really like this but the compatiblity seems poor. I think the closest emulation of that with good compatiblity is `function(...) { ... }.bind(this)`. – Mikko Rantalainen Apr 08 '16 at 07:42
  • 1
    This and any other ES6 feature is going to be need to be transpiled for the foreseeable future, but ES6 offers so many features and so much convenience that it's worth considering adding this step to your build process. –  Apr 08 '16 at 07:54
1

zoo is not a free floating variable, it's a property of the object. Inside test you can reference the object using this (because of the way you call it). Inside g the this context will be lost though, so you need to explicitly preserve it:

test: function(i) {
    var g = function(i) {
        alert(i + this.zoo);
    }.bind(this);
    g(i);
}

or:

test: function(i) {
    var g = function(i) {
        alert(i + this.zoo);
    };
    g.call(this, i);
}
deceze
  • 510,633
  • 85
  • 743
  • 889
1

When you invoke a member function of an object, the this keyword is set to the object (unless you call the function using .call or .apply). The function g is no longer a member of the object, so this is not set to the object. You have a couple of options if you want to keep using the g function.

Set a reference to this:

test: function(i) {
    var that = this;
    var g = function(i) {
        alert(i + that.zoo);
    }
    g(i);
} 

or manually set the value of this by using .call

test: function(i) {
    var g = function(i) {
        alert(i + this.zoo);
    }
    g.call(this, i);
} 

Here's a little more info about .call and .apply.

hargasinski
  • 841
  • 5
  • 14
0

How about :

({
      foo: 1,
      bar: 2,
      zoo: 3,
      test: function(i) {
        var g = function(_this,i) {
            alert(i + _this.zoo);
        }
        g(this,i);
      }
    }).test(2);
Anupam Singh
  • 1,158
  • 13
  • 25