Keyword "this" in Javascript

I always get tripped up by the this keyword in Javascript.And everytime when I “understand” it, I still feel confused about it next time.Hence, this post is here.

In fact, this follows a relatively small set of simple rules.

Gloabal context

In the global execution context, this refers to the global object, whether in strict mode or not.

console.log(this.document === document); // true

// In browser, the global object is the ```window``` object
// In Node.js, it's just called the "global object"
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

Function context

A function's this keyword may behaves a little differently in Javascript compared to other languages. And there are some cases where the value of this changes.

Method invocations

When a function is called as a method of an object, its this refers to the object the method is called on.

var creditCard = {
    money: 0,
    increment: function() {
        this.money += 8888;
    }
};

// dot notation
creditCard.increment();
console.log(creditCard.money);  // 8888

// bracket notation
creditCard['increment']();
console.log(creditCard.money); // 17776

/*****call the same function directly*****/
var moreMoney = creditCard.increment;
moreMoney();
console.log(creditCard.money); // 17776
console.log(money); // NAN

Note that when we call the same function directly, this refers to the global object instead of creditCard object.

// window.money += 8888, but window.money is undefined 
this.money += 8888;

And what's more, this rule also works on the prototype chain.

var moneyUp = {
    increment: function() {
        return this.money += 8888;
    }
};

var creditCard = Object.create(moneyUp);
creditCard.money = 0;

console.log(creditCard.increment());

The new operator

And when a function is used as a constructor(with the new keyword), its this is set to the newly created object.

function CreditCard() {
    this.money = 8888;
}

var savedMoney = new CreditCard();
console.log(savedMoney.money); // 8888
console.log(money);  // ReferenceError

// And without new, 
// this refers to the global object
var savedMoney_ = CreditCard();
console.log(savedMoney_.money); // undefined

Call and apply

All Javascript functions have two methods, call and apply, which let you call functions and explicitly set the value of this.

function add(c, d) {
    return this.a + this.b + c + d;
}

var o = {a:1, b:3};

//call
//The first parameter is the object to use as this
//and the second is an (optional) array of arguments 
add.call(o, 5, 7); // 1+3+5+7=16

//apply
//The first parameter is the object to use as this
//and the second should be passed individually 
add.appy(o, [10, 20]); // 1+3+10+20=34

And in this part, this is set to the first argument passed to call or apply inside function code when that function is called with either call or apply.

Bound functions

ECMAScript 5 introduced Function.prototype.bind .Calling f.bind(someObejct) creates a new function with the same body and scope as f, and has its this keyword set to the provided value, regardless of how the function is being used.

function f() {
    return this.nickname;
}

// a new function has its own this keyword
var g = f.bind({nickname: "Tinple"});
console.log(g()); // Tinple

var o = {nickname:"Kristine", f:f, g:g};
console.log(o.f(), o.g()); // Kristine, Tinple

Other conditions

DOM event handler

When a function is used as an event handler, its this is set to the element the event fired from (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener).

In-line event handler

When code is called from an in-line handler, its this is set to the DOM element on which the listener is placed.

<!-- button -->
<button onclick="alert(this.tagName.toLowerCase());">Show this</button>
<!-- the global window object-->
<button onclick="alert((function(){return this})());">Show inner this</button>

Note that only the outer code has its this set this way.

Eval breaks all the rules

As the code evaluated inside eval is its own type of executable code, the rules of determining what this refers to inside of eval code are a little more complex.

//depends on browser
eval.call({val: 0}, "console.log(this.val)");

And you can read more about eval here.

References: