Ранее говорилось о том, что при создании Лексического Окружения, например если была вызвана функция, формируется соответствующая Запись Окружения. И эта Запись Окружения содержит в себе не только информацию о переменных текущей области видимости, но и ключевое слово this, к которому можно обратиться напрямую из кода. Значение this динамически устанавливается JavaScript-движком на этапе создания контекста выполнения и указывает на объект, связанный с этим контекстом.

Значение this в глобальном контексте

Если говорить о глобальном контексте выполнения, который формируется при первом запуске программы, то в рамках этого контекста ключевое слово this будет ссылаться на глобальный объект Window. Например, если вы откроете в браузере Консоль Разработчика и введете this, то ответом вам вернется объект Window.

this

> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
this.a = 37;
console.log(window.a);

> 37

Значение this в контексте функции

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

function main() {
 console.log(this);
}

Здесь функция просто определена и еще не была вызвана, поэтому о значении this в рамках этой функции пока ничего нельзя сказать. Оно будет установлено во время вызова этой функции.

Простой вызов функции

В случае вызова этой функции не в строгом режиме, значением this будет являться глобальный объект Window.

function main() {
 console.log(this);
}
main(); // вызов функции

> Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

А в строгом режиме use strict, значением this будет undefined

function main() {
 "use strict";
 console.log(this);
}
main(); // вызов функции

> undefined

Вызов функции как метода объекта

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

let obj = {
 property: 10,
 method: function() {
  console.log(this);
  console.log(this.property);
 }
};
obj.method();

// this будет ссылаться на сам объект из переменной obj
> {property: 10, method: ƒ}
> 10

Здесь снова стоит заметить, что значение this не зависит от того, как и где функция была определена, а только от того, каким образом она была вызвана. Например, если определить функцию отдельно от объекта, а потом просто добавить её как метод в этот объект, то результат её вызова останется прежним.

let obj = {
 property: 10
};

function func() {
 console.log(this);
 console.log(this.property);
}
obj.method = func; // добавили функцию в объект
obj.method();

// this всё так же будет ссылаться на сам объект из переменной obj, методом которого была вызвана функция
> {property: 10, method: ƒ}
> 10

Другими словами значением this в данном случае является ближайший объект перед точкой. Может быть так, что объекты будут вложены друг в друга, например

function func() {
 console.log(this);
 console.log(this.property);
}

let obj = {
 property: 10,
 innerObj: {
     property:5,
     method: func
  }
};
obj.innerObj.method();

> {property: 5, method: ƒ}
> 5

В данном случае this ссылается на вложенный объект innerObj, так как именно его методом была вызвана функция. По другому вызов можно записать так

function func() {
 console.log(this);
 console.log(this.property);
}

let obj = {
 property: 10,
 innerObj: {
     property:5,
     method: func
  }
};

let target = obj.innerObj;
target.method();

> {property: 5, method: ƒ}
> 5

Поэтому тот факт, что innerObj является свойством другого объекта не имеет значения, важно лишь то, что функция вызывается именно в отношении этого объекта.

Явная передача контекста в функцию

Так же есть возможность при вызове функции явно передать ей значение this с помощью специальных выстроенных методов call, apply.

function add(c, d) {
 return this.a + this.b + c + d;
}

var obj = { a: 1, b: 3 };

// Здесь происходит вызов функции add с использованием встроенного метода call
// Первым параметром передаётся объект, на который будет ссылаться ключевое слово this в рамках функции add
// Следующими параметрами передаются значения аргументов функции (c и d соответственно)
let result1 = add.call(obj, 5, 7); // 1 + 3 + 5 + 7
console.log(result1); // 16

// Здесь происходит вызов функции add с использованием встроенного метода apply
// Первым параметром передаётся объект, на который будет ссылаться ключевое слово this в рамках функции add
// Следующим параметром передаётся значения аргументов функции в виде массива ( [c, d] )
let result2 = add.apply(obj, [10, 20]); // 1 + 3 + 10 + 20
console.log(result2); // 34

Первым параметром в них задаётся значение this, а последующими необязательными параметрами - аргументы, с которыми будет вызываться функция. Причем для метода call аргументы передаются списком через запятую fun.call(thisArg[, arg1[, arg2[, ...]]]). А для метода apply они передаются при помощи одного массива fun.apply(thisArg[, argsArray]).

Необходимо отметить,что если методам call и apply, передается значение с this, которое не является при этом объектом, то будет предпринята попытка преобразовать это значение в объект. Если переданное значение является примитивным типом, таким как 7 или 'строка', оно будет преобразовано в свой объектный аналог с использованием родственного конструктора, так примитив 7 преобразовывается в объект через new Number(7), а строка 'строка' в объект через new String('строка'), и т.д.

function func() {
  console.log(typeof this);
}

func.call(7); // примитив 7 будет преобразован в объектный аналог [object Number]

> object

Так же еще есть метод bind, который тоже позволяет явно передать контекст в функцию, но в отличие от методов call и apply не вызывает сразу функцию, а возвращает её обёртку. Об это методе будет рассказано в следующих частях курса.

Другие варианты установки this

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

Обнаружили ошибку или хотите добавить что-то своё в документацию? Отредактируйте эту страницу на GitHub!

Оставить комментарий