How does the this keyword in Javascript act within an object literal?
Javascript is a late binding language. In fact, it is very late binding. Not only is this
not bound during compile time, it is not even bound during runtime (as most other late binding languages do). In javascript, this
is bound during call time.
The binding rules are quite different from most other OO languages which is why it seems to confuse a lot of people not familiar with javascript.
Basically, how and where you use this
in the code does not affect how this
behaves (it does not matter if it's a standalone function, an object literal etc.) what determines the value of this
is how you call the function.
The rules are:
1 - When a function is called as a constructor, a new object is created and this
is bound to that object. For example:
function Foo () {
this.bar = 1; // when called with the new keyword
// this refers to the object just created
}
new Foo().bar;
2 - When called as an object method this
refers to the object the method belongs to. Basically the name before the last dot. For example:
foo.bar = 1;
foo.baz = function () {
alert(this.bar); // this refers to foo when called as foo.baz()
}
foo.baz();
3 - If used outside of any function or if a function is not called as a method this
refers to the global object. The javascript spec doesn't give a name to the global object apart from saying that one exists but for browsers it is traditionally called window
. For example:
bar = 1;
alert(this.bar); // this refers to the global object
foo = {
bar: this.bar // also global object
}
function foofoo () {
alert(this.bar); // also refers to the global object
}
foofoo();
4 - In an event handler (such as onclick etc.) this
refers to the DOM element that triggered the event. Or for events not associated with the DOM like setTimeout
or XMLHTTPRequest
, this
refers to the global object. For example:
foo.bar = 1;
foo.baz = function () {
alert(this.bar); // this would normally be foo but if this
// function is assigned to an event it would
// point to the element that triggered the event
}
somediv.bar = 2;
somediv.onclick = foo.baz; // clicking on somedive alerts 2 instead of 1
5 - Finally, when a function is called using either the call()
or apply()
methods this
can be reassigned to anything whatsoever (google "mdn function.prototype.call"). In this way, any object in javascript can borrow/steal another objects' methods. For example:
cat = {
type: "cat",
explain: function () {
return "I am a " + this.type;
}
}
dog = {
type: "dog"
}
cat.explain.call(dog); // returns "I am a dog"
With Function.bind()
in modern javascript implementations we now have another rule:
6 - Functions can also explicitly bind this
to an object using the bind()
method. The bind
method returns a new instance of the function where this
is bound to the argument passed to bind
. For example:
function explain () {
return "I am a " + this.type;
}
dog = {
type: "dog"
}
var dog_explain = explain.bind(dog);
dog_explain(); // returns "I am a dog"
ECMAscript 5 introduced strict mode which changes the meaning of this in functions that isn't called as a method or called with call or apply so we have to add a new rule:
7 - When in strict mode, this
isn't allowed to refer to the global object (window in browsers). So when a function is not called as a method or this
isn't bound to anything manually via call
or apply
or bind
then this
becomes undefined
:
"use strict";
function foo () {
return this;
}
foo(); // returns undefined instead of the global object
ECMAscript 6 introduced arrow functions. Arrow functions change how this behaves by binding early.
8 - In arrow functions, this
is bound at the time the function is declared. So this
in the following code:
var x = () => {return this};
behaves as if the function is declared like the following code:
var x = function () {return this}.bind(this);
Note that since the this
in arrow functions are bound at the time the function is declared you can't use arrow functions if you want to use inheritance. That's because the this
in the function will always point to the parent object and will never point to the child object. That means that the only way to make inheritance work with arrow function is to override all arrow functions from the parent object.
How does the this keyword work with objects in JavaScript?
The keyword this
is complicated. It is wise to avoid using it when there are better alternatives, which there usually are, because it has many gotchas.
With that out of the way, here's the answer: this
refers to the activation object (sometimes referred to as context object) that its containing function was called with.
What is the activation object? It is the object the function was attached to when it was called. The activation object for console.log()
is console
.
However...
const x = {
log : console.log
};
x.log();
... here x.log()
is exactly the same as console.log()
, except the activation object is x
instead of console
.
Seems pretty simple, right? It is, sort of. But there's more things to know.
- There is a default context object, used when a function is called without being attached to an object. It is the global object, known as
window
in the browser andglobal
in Node.js. - If the script is running in strict mode, then there is no default context object and it will be
undefined
if not explicitly called with a context - Arrow functions use lexical scoping and their
this
value is not the usual context object at all - it is the context object of its parent function from wherever the arrow function was defined
Now let's take the above and apply it to your code. Most importantly, your next()
method would work if it were called with the context being the range
object. The problem is, under the hood, the engine is basically doing this...
const returnedObject = range[Symbol.iterator]();
returnedObject.next();
... so next
is being called with returnedObject
as its context, rather than range
. But the function which returned the object is called with range
as its context. Thus, this
is different in each place.
You can actually fix your problem very easily by using an arrow function instead of a shorthand method.
This will work:
let range = {
from: 0,
to: 5
};
range[Symbol.iterator] = function() {
return {
next : () => {
if (this.from <= this.to) {
return { done: false, value: this.from++ };
} else {
return { done: true };
}
}
};
};
for (let num of range) {
console.log(num);
}
How does this keyword work within a function?
Cannibalized from another post of mine, here's more than you ever wanted to know about this.
Before I start, here's the most important thing to keep in mind about Javascript, and to repeat to yourself when it doesn't make sense. Javascript does not have classes (ES6 class
is syntactic sugar). If something looks like a class, it's a clever trick. Javascript has objects and functions. (that's not 100% accurate, functions are just objects, but it can sometimes be helpful to think of them as separate things)
The this variable is attached to functions. Whenever you invoke a function, this is given a certain value, depending on how you invoke the function. This is often called the invocation pattern.
There are four ways to invoke functions in javascript. You can invoke the function as a method, as a function, as a constructor, and with apply.
As a Method
A method is a function that's attached to an object
var foo = {};
foo.someMethod = function(){
alert(this);
}
When invoked as a method, this will be bound to the object the function/method is a part of. In this example, this will be bound to foo.
As A Function
If you have a stand alone function, the this variable will be bound to the "global" object, almost always the window object in the context of a browser.
var foo = function(){
alert(this);
}
foo();
This may be what's tripping you up, but don't feel bad. Many people consider this a bad design decision. Since a callback is invoked as a function and not as a method, that's why you're seeing what appears to be inconsistent behavior.
Many people get around the problem by doing something like, um, this
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
You define a variable that which points to this. Closure (a topic all it's own) keeps that around, so if you call bar as a callback, it still has a reference.
NOTE: In use strict
mode if used as function, this
is not bound to global. (It is undefined
).
As a Constructor
You can also invoke a function as a constructor. Based on the naming convention you're using (TestObject) this also may be what you're doing and is what's tripping you up.
You invoke a function as a Constructor with the new keyword.
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
When invoked as a constructor, a new Object will be created, and this will be bound to that object. Again, if you have inner functions and they're used as callbacks, you'll be invoking them as functions, and this will be bound to the global object. Use that var that = this trick/pattern.
Some people think the constructor/new keyword was a bone thrown to Java/traditional OOP programmers as a way to create something similar to classes.
With the Apply Method
Finally, every function has a method (yes, functions are objects in Javascript) named "apply". Apply lets you determine what the value of this will be, and also lets you pass in an array of arguments. Here's a useless example.
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
Using `this` with object literals
The way this
works is that it depends on how it is called. A quick tip to know what this
is is to look at the object before the function name in the call.
Doing apple.getInfo()
makes this
refer to apple
. But calling it as func()
is as if calling window.func()
(assuming it's in the global space), which makes this
refer to window
.
If you want to "force" the value of this
on a function, then do bind
. It creates a copy of the function with this
forced as the first parameter passed.
var func = apple.getInfo.bind(apple);
// all calls to `func` will have `this` "forced" as `apple`
If you want to dictate this
on the call but not permanently tamper this
on func
(like how bind
does it), you can go with call
or apply
:
var func = apple.getInfo;
func.call(apple);
func.apply(apple);
// Both function will have `this` as `apple` only for the call.
Self-references in object literals / initializers
Well, the only thing that I can tell you about are getter:
var foo = {
a: 5,
b: 6,
get c() {
return this.a + this.b;
}
}
console.log(foo.c) // 11
Value of this inside object method?
So, why the value of this inside
func2
is not inheriting the value of his enclosing scopeobj
?
The obj
here is not the "enclosing" scope. The scope that you are defining the obj
in is the "enclosing" scope.
Consider this example:
var obj = {
property: 5,
func1: function () {
let func2 = () => {
console.log(this.property);
}
func2();
},
}
obj.func1(); // logs 5
obj.func1.call({
property: 6
}) // logs 6
When the inner func2
is called, the this
keyword refers to the obj
as this
in the wrapper func1
function refers to the obj
and the func2
inherits the this
value. The inner arrow function does not bind it's own this
value.
Javascript: Object Literal reference in own key's function instead of 'this'
Both can be problematic.
var obj = {
key1: "it",
key2: function(){ return this.key1 + " works!" }
};
var func = obj.key2;
alert(func()); // error
When func
is not called as a method of obj
, this
can reference something else (in here: the global object "window
").
var obj = {
key1: "it",
key2: function(){ return obj.key1 + " works!" }
};
var newref = obj;
obj = { key1: "something else"; };
alert(newref.key2()); // "something else works"
In here we access the object from another reference, though the obj
in the function may now point to some other object.
So you will have to choose which case is more likely. If you really want to make it safe, prevent obj
from being exchanged:
// ES6 - use `const`:
const obj = {
key1: "it",
key2: function(){ return obj.key1 + " works always!" }
};
// ES5: use a closure where the `obj` is stored in a local-scoped variable:
var obj = (function(){
var local = {
key1: "it",
key2: function(){ return local.key1 + " works always!" }
};
return local;
})();
or you bind()
the function to the object:
var obj = {
key1: "it",
key2: function(){ return this.key1 + " works always!" }
}
obj.key2 = obj.key2.bind(obj);
Related Topics
Detecting an Undefined Object Property
Difference Between a Function Call and Function Reference
Generate Random String/Characters in JavaScript
How Does the "This" Keyword in JavaScript Act Within an Object Literal
How to Get the Caret Column (Not Pixels) Position in a Textarea, in Characters, from the Start
Generate Random Number Between Two Numbers in JavaScript
How to Check For "Undefined" in JavaScript
Generate Unique Random Numbers Between 1 and 100
Can Someone Explain the "Debounce" Function in JavaScript
Reactjs Component Names Must Begin With Capital Letters
What Does the Exclamation Mark Do Before the Function
How to Replace Plain Urls With Links
What Is the Use of the JavaScript 'Bind' Method
Updating a Nested Array With Mongodb
Async/Await Implicitly Returns Promise
How to Pass Props to {This.Props.Children}
How to Create an Array Containing 1...N
How to Check For an Empty/Undefined/Null String in JavaScript