Полный цикл в digital

Ключевое слово this

Поведение ключевого слова this зависит от контекста, в котором оно используется, и от того, в каком режиме оно используется - строгом или нестрогом.

Глобальный контекст и объект globalThis

В глобальном контексте this ссылается на глобальный объект. Что такое "глобальный объект" в JavaScript? Это зависит от среды, в которой выполняется код. Так, в веб-браузере this представляет объект window - объект, который представляет окно браузера. В среде Node.js this представляет объект global. А для веб-воркеров this представляет объект self.

Например, в веб-браузере при выполнении следующего кода:

console.log(this);

мы получим консольный вывод вроде следующего:

Window {window: Window, self: Window, document: document, name: "", location: Location, …}

В стандарт ES2020 было добавлено определение объекта globalThis, который позволяет ссылаться на глобальный конекст вне зависимости, в какой среде и в какой ситуации выполняется код:

console.log(globalThis);

Контекст функции

В пределах функции this ссылается на внешний контекст. Для функций, определенных в глобальном контексте, это объект globalThis:

function foo(){
var bar = "bar2";
console.log(this.bar);
}

var bar = "bar1";
// bar1
foo();

Если бы мы не использовали this, то обращение шло бы к локальной переменной, определенной внутри функции:

function foo(){
var bar = "bar2";
console.log(bar);
}

var bar = "bar1";
// bar2
foo();

Но если бы мы использовали строгий режим strict mode, то this в этом случае имело бы значение undefined:

"use strict";

function foo(){
var bar = "bar2";
console.log(this.bar);
}

var bar = "bar1";
// ошибка - this - undefined
foo();

Контекст объекта

В контексте объекта, в том числе в его методах, ключевое слово this ссылается на этот же объект:

var o = {
bar: "bar3",
foo: function(){
console.log(this.bar);
}
}

var bar = "bar1";
// bar3
o.foo();

Примеры this

Рассмотрим более сложный пример:

function foo(){
var bar = "bar2";
console.log(this.bar);
}

var o3 = {bar:"bar3", foo: foo};
var o4 = {bar:"bar4", foo: foo};
var bar = "bar1";

// bar1
foo();  
// bar3
o3.foo();  
// bar4
o4.foo();

Здесь определена глобальная переменная bar. И также в функции foo определена локальная переменная bar. Значение какой переменной будет выводиться в функции foo? Функция foo выводит значение глобальной переменной, так как данный скрипт запускается в нестрогом режиме, а значит ключеое слово this в функции foo ссылается на внешний контекст.

Иначе дело обстоит с объектами. Они определяют свой собственный контекст, в котором существует свое свойство bar. И при вызове метода foo внешним контекстом по отношению к функции будет контекст объектов o3 и o4.

Подобное поведение может привести к некоторому непонимаю в отдельных случаях. Так, рассмотрим другую ситуацию:

var o1 = {
bar: "bar1",
foo: function(){
console.log(this.bar);
}
}

var o2 = {bar: "bar2", foo: o1.foo};
var bar = "bar3";
var foo = o1.foo;

// bar1
o1.foo();  
// bar2
o2.foo();  
// bar3
foo();

Несмотря на то, что объект o2 использует метод foo из объекта o1, тем не менее функция o1.foo также будет искать значение для this.bar во внешнем котексте, то есть в контексте объекта o2. А в объекте o2 это значение равно bar: bar2.

То же самое с глобальной переменной foo, которая ссылается на ту же функцию, что и метод o1.foo. В этом случае также будет происходить поиск значения для this.bar во внешним контексте, то есть в глобальном контексте, где определена переменная var bar = bar3.

Однако если мы вызываем функцию из другой функции, вызываемая функция также будет использовать внешний контекст:

var bar = "bar2";

function daz(){
var bar = "bar5";
function maz(){
console.log(this.bar);
}
maz();
}

// bar2
daz();

Здесь функция daz в качестве this.bar использует значение переменной bar из внешнего контекста, то есть значение глобальной переменной bar. Функция maz также в качестве this.bar использует значение переменной bar из внешнего контекста, а это значение this.bar из внешней функции daz, которое в свою очередь представляет значение глобальной переменной bar. Поэтому в итоге консоль выведет bar2, а не bar5.

Явная привязка this

С помощью методов call() и apply() можно задать явную привязку функции к определенному контексту:

function foo(){
console.log(this.bar);
}

var o3 = {bar: "bar3"}
var bar = "bar1";

// bar1
foo();  
// bar3
foo.apply(o3);  
// или
// foo.call(o3);

Во втором случае функция foo привязывается к объекту o3, который и определяет ее контекст. Поэтому во втором случае консоль выведет bar3.

Метод bind

В основном, мы используем метод bind(), чтобы вызывать функцию с указанием значения this. Метод позволяет легко выставлять какой именно объект будет привязан к this в момент вызова функции или метода.

Обычно нам требуется bind() тогда, когда мы используем в методе this и вызываем сам метод из получающего объекта. В таких случаях this не привязывается к предполагаемому объекту, что само собой ведет к ошибке в работе кода.

В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать this. Вызов происходит с фиксированным this:

var o = {
bar: "bar3",
foo: function () {
console.log(this.bar);
}.bind(bar),
};

var bar = "bar1";

// bar1
o.foo();
function foo() {
console.log(this.bar);
}

var bar1 = { bar: "bar1" };
var bar = "bar2";

// bar2
foo();  

var func = foo.bind(bar1);
// bar1
func();

this и стрелочные функции

В стрелочных функциях объект, передаваемый через this, берется из родительского контекста, в котором определена стрелочная функция, рассмотрим следующий пример:

const person = {
name: "Tom",
say:()=> console.log(`Меня зовут ${this.name}`)
}

// Меня зовут
person.say();

Здесь стрелочная функция say() обращается к некому свойству this.name, но что здесь представляет this? Для внешнего контекста, в котором определена стрелочная функция - то есть для контекста объекта person this представляет глобальный объект (объект окна браузера). Однако глобальной переменной name не определено, поэтому на консоль будет выведено:

Меня зовут

Теперь немного изменим пример:

const person = {
name: "Tom",
hello(){
console.log("Привет");
let say = ()=> console.log(`Меня зовут ${this.name}`);
say();
}
}

person.hello();

Теперь стрелочная функция определена в методе hello(). this для этого метода представляет текущий объект person, где определен данный метод. Поэтому и в стрелочной функции this будет представлять объект person, а this.name - свойство name этого объекта. Поэтому при выполнении программы мы получим:

Привет
Меня зовут Tom

Несмотря на то, что стрелочные функции могут добавить забот при работе this, в то же время они могут решить ряд проблем. Так, при работе с несколькими контекстами мы вынуждены учитывать, в каком контексте определяется переменная. Например, возьмем следующий код:

const school ={
title: "Oxford",
courses: ["JavaScript", "TypeScript", "Java", "Go"],
printCourses: function(){
this.courses.forEach(function(course){
console.log(this.title, course);
})
}
}

school.printCourses();

Функция printCourses проходит по всем курсам из массива и при их выводе предваряет их значением свойства title. Однако на консоли при запуске программы мы увидим следующее:

undefined "JavaScript"
undefined "TypeScript"
undefined "Java"
undefined "Go"

Мы видим, что значение this.title не определено, так как this как контекст объекта замещается глобальным контекстом. В этом случае нам надо передать подобное значение this.title или весь контекст объекта.

const school ={
title: "Oxford",
courses: ["JavaScript", "TypeScript", "Java", "Go"],
printCourses: function(){
const that = this;
this.courses.forEach(function(course){
console.log(that.title, course);
})
}
}

school.printCourses();

Стрелочные функции также позволяют решить данную проблему:

const school ={
title: "Oxford",
courses: ["JavaScript", "TypeScript", "Java", "Go"],
printCourses: function(){
this.courses.forEach((course)=>console.log(this.title, course))
}
}

school.printCourses();

Контекстом для стрелочной функции в данном случае будет выступать контекст объекта school. Соответственно, нам недо определять дополнительные переменые для передачи данных в функцию.

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг