3

Given this code:

const numberManipulator = (function() {
   this.defaultNumber = 5;

  const doubleNumber = function(num) {
    console.log(this.defaultNumber);
    return num !== undefined ? num * 2 : new Error('no number provided!');
  }

  return {
    doubleNumber
  }
})();

const test = numberManipulator.doubleNumber(20);

Why is console.log(this.defaultNumber showing undefined but if I console.log(defaultNumber, it shows 5? I thought that it would show undefined for the latter.

Thanks

user1354934
  • 6,033
  • 10
  • 40
  • 61

2 Answers2

3

You should read How does the “this” keyword work?

When the IIFE is called, its this is not set so it defaults to the global object, so in:

const numberManipulator = (function() {
   this.defaultNumber = 5;
...
})();

this.defaultNumber = 5 creates a property of the global (window in a browser) object called defaultNumber and assigns it a value of 5.

If the return statement should really be:

return {doubleNumber: doubleNumber}

then the IIFE returns an object reference to numberManipulator. When called as:

numberManipulator.doubleNumber(20)

then this within doubleNumber is numberManipulator, which doesn't have a defaultNumber property so this.defaultNumber returns undefined.

Community
  • 1
  • 1
RobG
  • 124,520
  • 28
  • 153
  • 188
1

Long story short:

1. The IIFE is executing in the context of the window I was wrong, see RobG's comment.
2. this thus refers to the window inside the IIFE, since it is not set by default.
3. Your doing this.defaultNumber = 5; is hence equivalent to
window.defaultNumber = 5;
4. The second method executes in the context of numberManipulator and so logging this.defaultNumber is equivalent to numberManipulator.defaultNumber which was never set by you. Hence, it is undefined.

Explanation on this:

Inside any function, the this keyword refers to the current context in which the function is executing. It doesn't matter where you defined that function inside your codebase. All it matters in what context are you calling it. In your current case, you're calling the IIFE in the context of the window. Doing:

const numberManipulator = (function() {
   // MAGIC
})();

amounts to:

  1. running an IIFE in the window context.
  2. assigning the result it returned to a constant.

However, the context of a function may be changed by using .call, .apply, or by simply calling that method as a property of another object. In your second log, this.defaultNumber, this refers to the object since you called the method using numberManipulator.doubleNumber(20);. Calling methods using object.prop() sets their context (=this) to that object.

Hope that helps! :D

Gaurang Tandon
  • 5,704
  • 9
  • 37
  • 73
  • Your logical connection between 1 and 2 is flawed. All code is executed in either a global, function or eval context. How *this* is set is irrelevant to the execution context, except for arrow functions which adopt the *this* of their enclosing context. Running the IIFE in a function context would still resolve *this* to the global object (except in strict mode, where it would resolve to *undefined* regardless of the context). See [*ECMA-262: Execution Contexts*](http://www.ecma-international.org/ecma-262/7.0/index.html#sec-execution-contexts) – RobG Mar 24 '17 at 06:36
  • @RobG Oh, sorry. Thanks for the info. I edited my answer. – Gaurang Tandon Mar 24 '17 at 08:27