Understanding the this property
The this keyword is an object property. When used within a function, it takes the form of the object to which the function is bound at invocation.
In every JavaScript environment, we have a global object. In Node.js, the global object is named global and, in the browser, the global object is named window.
By global object, we mean that all the variable declarations and functions are represented as a property and method of this global object. For example, in a browser script file, we can access the global objects, as shown in the following code snippet:
name = "Dale" function print() { console.log("global") } // using the browser as our environment console.log(window.name) // Dale window.print() // global
In the preceding code block, the name
variable and print
function are declared at the global scope, hence they can be accessed as an attribute (window.name
) and method (window.print()
) of the window global object.
The statement made in the previous sentence can be summarized as the global name and function are binded (or assigned) by default to the global object window.
This also means that we can always bind this variable to any object having the same name
variable and the same function, called print
.
To get this concept, first, let's re-write window.print()
as print.call(window)
. This new method is called de-sugaring in JavaScript; it is like seeing an implementation of a method in its real form.
The .call
method simply takes in the object we want to bind a function call to.
Let's see how print.call()
and how this property works. We'll rewrite the print
function to access the name
variable, as shown in the following code snippet:
name = "Dale" object_name = "window" function print(){ console.log(`${this.name} is accessed from ${this.object_name}`) } console.log(print.call(window)) // Dale is accessed from window
Now, let's create a custom object and also give it the same property as the window
object, as shown in the following code snippet:
let custom_object = { name: Dale, Object_name: "custom_object" } print.call(custom_object) // Dale is accessed from custom_object
This concept can be applied to all Object methods, as shown in the following code:
data = { name: 'Dale', obj_name: 'data', print: function () { console.log(`${this.name} is accessed from ${this.obj_name}`); } } data.print() // Dale is accessed from data // don't forget we can also call print like this data.print.call(data) // Dale is accessed from data
With this, we can also bind the print()
method from data
to another object, as shown in the following code snippet:
let data2 = { name: "Dale D" Object_name: "data2" } data.print.call(data2) // Dale D is accessed from data2
This method shows how this property depends on the function invocation runtime. This concept also affects how some event operations work in JavaScript.
Further reading
To get a deeper understanding of this concept, Yehuda Katz, one of the creators of Emberjs and Members of TC39, sheds more light on this in his article, Understanding JavaScript Function Invocation and "this".