4
var num = 1;

var obj = {

    num: 2,

    getNum: function() {
        return (function() {
            return this.num;
        })();
    }

}

console.log(obj.getNum()); // 1

the JavaScript code's result is 1. So this refers to global object rather than obj.

Deryckxie
  • 175
  • 1
  • 1
  • 8

4 Answers4

2

All else being equal, this refers, when used directly within a function, to the object of which the function is a method (member), when that function is called in the typical way of obj.method(). Directly means directly; use within a function nested within the method, whether anonymous or immediately-invoked or otherwise, does not qualify.

In your case

return (function() {
    return this.num;
})();

the function is not a member of any object, hence there's no this, hence this is the global object (or null in strict mode).

Just because the function where you are using this is nested within a function which is a method is not enough. The function where you are using this must be the method itself.

To get the behavior you want, you have several alternatives:

getNum: function() {
  return (() => this.num)();
}

This works because fat arrow functions use lexical this.

Another idea, already shown in another answer:

getNum: function() {
  var self = this;
  return function() {
    return self.num;
  }();
}

In this context, by the way, there is no need to enclose the function in parentheses to force it to be a function expression that you can call immediately. Merely by virtue of being on the right side of the :, it's already an expression.

Yet a third approach:

getNum: function() {
  return function() {
    return this.num;
  }.call(this);
}

Here we are forcing the this of the anonymous function to be the this of getNum, by explicitly using call.

For the definitive answer, see How does the "this" keyword work?.

Note on the term "closure"

In your question, you appear to be using the term "closure" to mean "nested function". Although your nested, anonymous, immediately-invoked function may be a closure in some extremely narrow technical sense, using the term this way is likely to confuse both yourself and others. Your function does not satisfy either of the two following generally-accepted criteria for being a closure:

  1. It does not "close over" (use) any variables from the surrounding scope.

  2. It is not returned to the outside world as a function value carrying along with it references to such variables from the surrounding lexical scope.

Hence, it would be much clearer to simply refer to this function as a "nested function".

Community
  • 1
  • 1
0

Inside a function this depends on the execution context, so it's important how the function is called.

If the calling context does not set this, it's different in strict and non-strict mode.

In non-strict mode, it refers to the global object. In strict mode, its undefined. Note that undefined can be actually set to something else.

To set the said context, you need to invoke function as a property of an object, as in obj.getNum() invocation. In would give an object scope to the function, but it does not have anything to do with another function call in which you are trying actually access this.

Following code illustrates it:

var num = 1;
var obj = {
  num: 2,
  getNum: function() {
    var outerThis = this;
    return (function() {
      return {
        innerThis: this,
        outerThis: outerThis,
        innerNum: this.num,
        outerNum: outerThis.num
      };
    })();
  },
  getNumStrict:function() {
    "use strict"
    var outerThis = this;
    return (function() {
      return {
        innerThis: this,
        outerThis: outerThis,
        outerNum: outerThis.num
      };
    })(outerThis.num);
  }
}
console.log(obj.getNum());
// Object {innerThis: Window, outerThis: Object, innerNum: 1, outerNum: 2}

console.log(obj.getNumStrict());
// Object {innerThis: undefined, outerThis: Object, outerNum: 2}

As you can see, this is a controversal concept in JavaScript, and you can easily get by without it.

To make private properties private you can create them in a function scope and return a function returning a copy of an interface instead of an object reference:

var num = 1;
var createHaveNum = function(initial) {
  var num = initial;
  var getInterface;
  var setNum = function(x) {
    num = x;
    console.log("setting num");
  };
  var getNum = function() {
    console.log("getting num");
    return num;
  };
  getInterface = function() {
    return {
      getNum: getNum,
      setNum: setNum
    };
  };
  console.log("Instance is initialized.");
  return getInterface;
};

var obj = createHaveNum(2); // create an object, obtain a reference in form of a function

var i = obj(); // obtain an interface
var j = obj(); // do it again, interface to same object
j.setNum = undefined; // does not break internal state, object is intact
console.log(i.getNum());
i.setNum(3);
var getNum = i.getNum; // reference function
console.log(getNum()); // invoke without a scope, no problem
var other = {getNum: i.getNum}; // put in another object
console.log(other.getNum()); // call with another context, no problem - same scope as before.
/*
Instance is initialized.
getting num
2
setting num
getting num
3
getting num
3
*/
George Polevoy
  • 6,775
  • 3
  • 30
  • 58
-1

Because obj does not exist when the object literal is defined, so it can't be closed over. On top of that, closure operates on the scope, and you don't have a scope outside the function.

At the moment the literal is being evaluated, there is no obj. When evaluating that statement, the object literal is evaluated first (the right-hand operand to the = operator must be evaluated first), then assigned to the newly-declared variable obj.

To make matters worse, closure in ES is on the scope and ES5 (and below) only have function-scope. You don't have an outer scope to close over, besides the global scope.

ssube
  • 41,733
  • 6
  • 90
  • 131
  • 2
    Could you please elaborate on what you mean? – BadHorsie Sep 14 '15 at 15:55
  • If you log `this.num` _outside_ the IIFE (but inside of `getNum`), it logs the correct value (`2`). Can you explain why the reference changes _inside_ the IIFE? – Mathletics Sep 14 '15 at 15:58
  • Thanks, I understand. I was somewhat confused what you were referring to in the original version of your answer. – BadHorsie Sep 14 '15 at 15:59
-1

To get the "desired" result, you'd have to redefine getName as below:

getNum: function() {

    var that = this; //the enclosing object
    return (function() {

        return that['num'];

    })();

}
NaijaProgrammer
  • 2,813
  • 2
  • 19
  • 32