5.8 KiB
title |
---|
this reference |
this
reference
In JavaScript, every function has a this
reference automatically created when you declare it. This reference is quite similar to this
reference in other class-based languages such as Java or C# (JavaScript is a prototype-based language and no "class" concept): It points to the which object is calling to the function (this object sometimes called as context). In JavaScript, however, the this
reference inside functions can be bound to different objects depending on where the function is being called. Here are 5 basic rules for this
binding in JavaScript:
Rule 1
When a function is called in the global scope, the this
reference is by default bound to the global object (window
in the browser, or global
in Node.js). For example:
function foo() {
this.a = 2;
}
foo();
console.log(a); // 2
Note: If you declare the foo()
function above in strict mode, then you call this function in global scope, this
will be undefined
and assignment this.a = 2
will throw Uncaught TypeError
exception.
Rule 2
Let's examine example below:
function foo() {
this.a = 2;
}
var obj = {
foo: foo
};
obj.foo();
console.log(obj.a); // 2
Clearly, in the above snippet, the foo()
function is being called with context is obj
object and this
reference now is bound to obj
. So when a function is called with a context object, the this
reference will be bound to this object.
Rule 3
.call
, .apply
and .bind
can all be used at the call site to explicitly bind this
. Using .bind(this)
is something you may see in quite a lot of React components.
var foo = function() {
console.log(this.bar)
}
foo.call({ bar: 1 }) // 1
Here's a quick example of how each one is used to bind this
:
.call()
:fn.call(thisObj, fnParam1, fnParam2)
.apply()
:fn.apply(thisObj, [fnParam1, fnParam2])
.bind()
:const newFn = fn.bind(thisObj, fnParam1, fnParam2)
Rule 4
function Point2D(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point2D(1, 2);
console.log(p1.x); // 1
console.log(p1.y); // 2
The thing you must notice that is the Point2D
function called with new
keyword, and this
reference is bound to p1
object. So when a function is called with new
keyword, it will create a new object and this
reference will be bound to this object.
Note: As you call a function with new
keyword, we also call it as constructor function.
Rule 5
JavaScript determines the value of this
at runtime, based on the current context. So this
can sometimes point to something other than what you expect.
Consider this example of a Cat class with a method called makeSound()
, following the pattern in Rule 4 (above) with a constructor function and the new
keyword.
var Cat = function(name, sound) {
this.name = name;
this.sound = sound;
this.makeSound = function() {
console.log( this.name + ' says: ' + this.sound );
};
}
var kitty = new Cat('Fat Daddy', 'Mrrooowww');
kitty.makeSound(); // Fat Daddy says: Mrrooowww
Now let's try to give the cat a way to annoy()
people by repeating his sound 100 times, once every half second.
var Cat = function(name, sound) {
this.name = name;
this.sound = sound;
this.makeSound = function() {
console.log( this.name + ' says: ' + this.sound );
};
this.annoy = function() {
var count = 0, max = 100;
var t = setInterval(function() {
this.makeSound(); // <-- this line fails with `this.makeSound is not a function`
count++;
if (count === max) {
clearTimeout(t);
}
}, 500);
};
}
var kitty = new Cat('Fat Daddy', 'Mrrooowww');
kitty.annoy();
That doesn't work because inside the setInterval
callback we've created a new context with global scope, so this
no longer points to our kitty instance. In a web browser, this
will instead point to the Window object, which doesn't have a makeSound()
method.
A couple of ways to make it work:
- Before creating the new context, assign
this
to a local variable namedme
, orself
, or whatever you want to call it, and use that variable inside the callback.
var Cat = function(name, sound) {
this.name = name;
this.sound = sound;
this.makeSound = function() {
console.log( this.name + ' says: ' + this.sound );
};
this.annoy = function() {
var count = 0, max = 100;
var self = this;
var t = setInterval(function() {
self.makeSound();
count++;
if (count === max) {
clearTimeout(t);
}
}, 500);
};
}
var kitty = new Cat('Fat Daddy', 'Mrrooowww');
kitty.annoy();
- With ES6 you can avoid assigning
this
to a local variable by using an arrow function, which bindsthis
to the context of the surrounding code where it's defined.
var Cat = function(name, sound) {
this.name = name;
this.sound = sound;
this.makeSound = function() {
console.log( this.name + ' says: ' + this.sound );
};
this.annoy = function() {
var count = 0, max = 100;
var t = setInterval(() => {
this.makeSound();
count++;
if (count === max) {
clearTimeout(t);
}
}, 500);
};
}
var kitty = new Cat('Fat Daddy', 'Mrrooowww');
kitty.annoy();