The Mystery of this

Overview

Invocation types in JavaScript

function foo(msg) {
    console.log(msg);
}

function AFoo(msg) {
    console.log(msg);
}

function MyFoo() {
    return {
        aFoo: function (msg) {
            console.log(msg);
        }
    }
}

// Function invocation
foo('Function Invocation');

// Indirect Invocation
foo.call(undefined, 'Indirect Invocation');

// Constructor Invocation
new AFoo('Constructor Invocation');

// Method Invocation
new MyFoo().aFoo('Method Invocation!');

Function Invocation

  • this is the global object in a function invocation
(function () {
    console.log(this);  // window in browser, global in node
})();
// Object [global] {
//   global: [Circular],
//   clearInterval: [Function: clearInterval],
//   ... etc
// }
  • If you assign a function to an object and invoke it, this is the object itself
function f() {
    console.log(this);
}

let myObj = {
    myMethod: f
};

myObj.myMethod();
// { myMethod: [Function: f] }

Method Invocation

  • this is the object itself
let foo = {
    foo: function () {
        console.log(this);
    }
};

foo.foo();
// { foo: [Function: foo] }
  • this in an inner function invocation will refer to the global object
let foo = {
    foo: function () {
        function bar() {
            console.log(this);
        }
        bar();  // Function invocation!
    }
};

foo.foo();
// We are invoking a method, which ends up in a function invocation
// this will be the global object
  • You can bind the object to the function for this to refer to the object
let foo = {
    foo: function () {
        function bar() {
            console.log(this);
        }
        bar.bind(this)();
    }
};

foo.foo();
// { foo: [Function: foo] }
  • You can do an indirect call on the function
let foo = {
    foo: function () {
        function bar() {
            console.log(this);
        }
        bar.call(this);  // not bar()..
    }
};

foo.foo();
// { foo: [Function: foo] }
  • If you assign a method to a variable and invoke, this is back to global object
let foo = {
    foo: function () {
        console.log(this);
    }
};

let bar = foo.foo;

// The following refers to a method, but this is a function invocation
bar();
// Object [global] {
//   global: [Circular],
//   clearInterval: [Function: clearInterval],
//   ... etc
// }
  • You can bind the object to the method in order to keep this to refer to object
let foo = {
    foo: function () {
        console.log(this);
    }
};

let bar = foo.foo.bind(foo);

bar();
// { foo: [Function: foo] }
  • Another example with bind
function Foo(name) {
    this.name = name;
    this.info = function () {
        console.log(this.name);
    }
}

let myFoo = new Foo('myFoo');
setTimeout(myFoo.info.bind(myFoo), 100);  // If you do not bind, undefined will be printed
                                          // since this will refer to global object