This

The most infamous Javascript keyword

                
                    var self = this; // Dammi una lametta...
                
            

This keyword can be used everywhere, not only in objects:

  • In functions
  • Outside functions (top-level scope)
  • In a string passed to eval() (not covered here)

In functions

The most common use are:

  • Functions (this refers to the global object, undefined in strict mode)
  • Constructors (this refers to the object instance)
  • Methods (this refers to the receiver of the method call)

Functions

When you call a function the value of this depends on the mode: normal or strict

In normal execution mode this point to the global object:
window in browsers, global in Node.js or other server side environment.
function log() {
    console.log(this);
}

log(); // window
                

In strict mode the value of this is undefined

                    
'use strict';

function log() {
    console.log(this);
}

log(); // undefined
                

We can arbitrarily change the value of this with two function:

  • Function.prototype.call
  • Function.prototype.apply
                    
let robot = {
    name: 'Robocop'
};

function kill(toKill) {
    console.log(this.name + ' will kill ' + toKill);
}

kill.call(robot, 'you')     // Robocop will kill you
kill.apply(robot, ['you'])  // Robocop will kill you
                

Constructors

A constructor is a function invoked with the new operator that return the instance of an object.

When a constructor is invoked with new, this is bound to the newly created object.

                    
function Dog(name) {
    this.name = name;
}

let pluto = new Dog('Pluto');

pluto.name; // 'Pluto'
                

Methods

this refers to the receiver, the object on which the method has been called.

                    
let obj = {
    prop: 'a prop',
    method() {
        console.log(this.prop);
    }
};

obj.method(); // 'a prop'
                

Top level scope

In browsers, the top-level scope is the global scope and this refers to the global object (window)

                
                    
            

Common mistakes

  • Forgetting new operator
  • Shadowing the value of this
  • Extracting methods invocation

Forgetting new operator

When you call a constructor without the new operator you are invoking it like a real function!

                    
function Point(x, y) {
    this.x = x;
    this.y = y;
}

let point = Point(10, 20);

// Constructor doesn't return the object instance!
console.log(point); // undefined

// Global variables created! Gomblotto!1!1
window.x;   // 10
window.y;   // 20
                

Fix

                    
'use strict';

function Point(x, y) {
    this.x = x;
    this.y = y;
}

// TypeError: Cannot set property 'x' of undefined
let point = Point(10, 20);
                

Shadowing the value of this

Sometimes we forgot about Javascript lexical scope!

                    
let component = {
    width: 800,
    init() {
        document.addEventListener('click', function(e) {
            console.log(this.width);
        });
    }
};

// On document's click this.width is undefined
component.init();
                

FIX

We can store the value of this

                    
let component = {
    width: 800,
    init() {
        // Woooooo
        let self = this;

        document.addEventListener('click', function(e) {
            console.log(self.width);
        });
    }
};

component.init();
                

FIX

We can use a sexy arrow function*

                    
let component = {
    width: 800,
    init() {
        document.addEventListener('click', e => {
            console.log(this.width);
        });
    }
};

component.init();
                
*only for ES6 brave guys!

FIX

Or use Function.prototype.bind

                    
let component = {
    width: 800,
    init() {
        document.addEventListener('click', function(e) {
            console.log(this.width);
        }.bind(this));
    }
};

component.init();
                

Extracting methods invocation

If we retrieve the value of a method, instead of invoking it, we turn that method into a function
pointing this to the global object!

                    
let counter = {
    count: 0,
    increment() {
        this.count++;
        console.log(this.count);
    }
};

setInterval(counter.increment, 1000);
                

Fix

Use Function.prototype.bind to create a new function with this bound to the right object.

                    
let counter = {
    count: 0,
    increment() {
        this.count++;
        console.log(this.count);
    }
};

setInterval(counter.increment.bind(counter), 1000);
                

Thank you!

Exercises

The hidden slide on eval!

eval function can be called:

  • Directly
  • Indirectly

When eval is called directly this point to surrounding eval context

                
let x = 'global';

function directly() {
    let x = 'local';
    console.log(eval('x')); // local
}

directly();

// Point depending on the context;
function nonStrict() {
    console.log(eval('this')); // window
}
nonStrict();

function strict() {
    'use strict';
    console.log(eval('this')); // undefined
}
strict();
                

When eval is called indirectly this always point to the global object

                
let x = 'global';

function indirectly() {
    let x = 'locals';
    console.log(window.eval('x'));
}

indirectly();

// Let's try with this
let obj = {
    indirectly: function() {
        console.log(window.eval('this'));
    }
};

obj.indirectly();