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: