If you want to study OOP in JavaScript thoroughly, you need to understand the concept of this
. Nearly everything else is based on context, including how methods and classes work.
What complicates things a bit is that context works differently for normal and arrow functions. And since arrow functions appeared later in the language, you need to start learning context with regular functions in order to get to grips with the topic. A little reminder of what such features look like:
// Defining the arrow function and assigning it to a constant
const f = () => 'i am an arrow function';
// Defining a normal anonymous function
function() {
return 'i am a regular function without name';
}
// Defining an ordinary named function
function f() {
return 'i am a regular function with name';
}
In this lesson, we'll only look at normal functions, arrow functions will be in one of the next lessons. Normal functions work with context the same way, whether they're named or not.
In JavaScript, functions behave like data: they can be written to variables, constants, and even object properties. Functions written inside object properties are called methods:
const company = { name: 'Hexlet' };
// Creating a function that is immediately assigned to the getName property and becomes a method
company.getName = function() {
return 'Hexlet';
};
// Method call
company.getName(); // "Hexlet"
This is just one of the many possible ways to add a function to an object. Below are a few more examples:
// When an object is created
const obj = {
getName: function() {
return 'Hexlet'
},
};
// Via assignment to constant
const company = { name: 'Hexlet' };
function getHexlet() {
return 'Hexlet';
};
// The name isn't important
company.getName = getHexlet;
company.getName(); // "Hexlet"
All of the options above are equivalent. They lead to the same result, but there is one catch. The method returns a string and does not use object data in any way. If the name is changed, the method will continue to return the value stored in it rather than the current name of the company within the object.
company.getName(); // "Hexlet"
company.name = 'Hexlet Plus';
// The name has been changed, but obviously, what it returns remains the same
company.getName(); // "Hexlet"
To solve this problem, we need to access the object data inside the method. This is done through a special keyword called this
denoting context. It refers to the current object to which the method is associated.
const company = { name: 'Hexlet', employees: [] };
company.getName = function getName() {
return this.name;
};
company.getName(); // "Hexlet"
company.name = 'Hexlet Plus';
company.getName(); // "Hexlet Plus"
this
makes it possible not only to read the data, but also to change it:
company.setName = function setName(name) {
this.name = name;
};
company.getName(); // "Hexlet"
company.setName('Hexlet Plus');
company.getName(); // "Hexlet Plus"
Another example, with a change to the internal array in the object:
// Adding a new employee
company.addEmployee = function addEmployee(user) {
// It's important for the employees to have already been added to the company at the time of the call
this.employees.push(user);
};
const user = { name: 'Peter' };
company.addEmployee(user);
company.employees; // [{ name: 'Peter' }]
// Or via the method
company.getEmployees = function() {
return this.employees;
};
company.getEmployees(); // [{ name: 'Peter' }]
As you can see from the examples above, properties can be changed either directly or from methods. Which way to go depends on the situation. With further courses and experience, you'll start to understand better which way to prefer.
Context
Earlier, when the definition of this
, was given, it was said that this
refers to the current object, to which the method is bound to. And this is where the key difference between this
in JavaScript and this
in other languages lies. In JavaScript, a method this can change:
const company1 = { name: 'Hexlet', getName: function getName() { return this.name } };
const company2 = { name: 'Hexlet Plus' };
company1.getName(); // "Hexlet"
company2.getName = company1.getName;
// In both cases it's the same function
company2.getName(); // "Hexlet Plus"
company1.getName(); // "Hexlet"
What's happened here? Calling the same function from another object changed the object referenced by this
. This feature is called late binding. The value of this
refers to the object from which the method is called.
Looking at how JavaScript functions are called and where this
comes from can help you comprehend this feature the best. . comes from. Since JavaScript functions are also objects, they have their own methods. Among them is the call()
, method, which is used to, as you might expect, call:
const sayHi = () => 'Hi!';
sayHi.call(); // "Hi!"
Why is it done that way? The point is that the first parameter of this function takes a context, an object that will be referenced by this
inside the function. Functions don't have to be a method to do this:
const getName = function getName() {
return this.name;
};
const company1 = { name: 'Hexlet' };
// The function is called directly, it's not a method
getName.call(company1); // "Hexlet"
const company2 = { name: 'Hexlet Plus' };
getName.call(company2); // "Hexlet Plus"
That's the whole secret of this
. This is the context that JavaScript throws automatically to a function if it's called as a method. In this case, you can tell exactly which object it belongs to.
Now that you know how this
, works, try to answer this question: what will be displayed on the screen?
const company = {
name: 'Hexlet',
country: {
name: 'Finland',
getName: function getName() {
return this.name;
}
},
};
console.log(company.country.getName()); // => ?
The correct answer is "Finland"
. Why? Because the context for the getName()
method is a country
, not a company
object. If you modify the code a little bit, it will be easier to understand this idea:
const { country } = company;
console.log(country.getName()); // "Finland"
An shorthand method definition
Because of the need to use normal functions when creating objects, JavaScript has introduced a special abbreviated syntax for creating methods when defining objects:
const company = {
name: 'Hexlet',
getName() {
return this.name;
},
// Same as
// getName: function getName() {
// return this.name;
// },
};
This way is just “syntactic sugar”. It allows you to shorten the notation, but it has no effect on the behavior. The main thing to remember is that this is a regular function, not an arrow function. This is the definition we will use from now on.
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.