JavaScript. Контекст вызова функции
17.06.2021
Теги: JavaScript • Web-разработка • Теория • Функция
В JavaScript this — это текущий контекст исполнения функции, он определяется в момент вызова. Функцию можно вызвать четырьмя способами и каждый из них определяет свой контекст. Кроме того, режим strict также влияет на контекст исполнения. Рассмотрим каждый способ вызова функции и посмотрим, на что будет указывать this в каждом из них.
- Простой вызов функции —
hello('Привет') - Вызов метода объекта —
user.hello('Привет') - Вызов с указанием контекста —
hello.call(user, 'Привет') - Вызов конструктора —
new User('Вася')
1. Простой вызов функции
Здесь все просто — контекстом вызова будет глобальный объект window или undefined, в зависимости от режима strict.
function foo() { console.log(this); // window bar(); function bar() { console.log(this); // window } } foo();
'use strict'; function foo() { console.log(this); // undefined bar(); function bar() { console.log(this); // undefined } } foo();
2. Вызов метода объекта
Метод — это функция, хранящаяся в объекте. При вызове метода this — это объект, которому принадлежит метод.
var user = { name: 'Вася', hello: function () { console.log('Привет,', this.name); } }; user.hello(); // Привет, Вася
Ловушка — потеря контекста
Метод объекта можно переместить в отдельную переменную. При вызове метода с использованием этой переменной можно подумать, что this — это объект, в котором определён метод. На самом деле, если метод вызван без объекта, происходит простой вызов функции, и this становится глобальным объектом window или undefined.
var user = { surname: 'Иванов', show: function () { console.log('Фамилия', this.surname); } }; setTimeout(user.show, 1000); // Фамилия undefined
Фактически, последняя строка может быть переписана как:
var f = user.show; setTimeout(f, 1000); // контекст user потерян
Метод setTimeout() в браузере имеет особенность — он устанавливает this=window для вызова функции. Таким образом, для this.surname он пытается получить window.surname, которого не существует. В других подобных случаях this обычно просто становится undefined.
Самый простой способ обойти ловушку — это обернуть вызов в анонимную функцию, создав замыкание:
var user = { surname: 'Иванов', show: function () { console.log('Фамилия', this.surname); } }; setTimeout(function () { user.show(); }, 1000); // Фамилия Иванов
Теперь объект user достаётся из замыкания, а затем вызывается его метод show().
3. Вызов с указанием контекста
Методы call() и apply() объекта Function позволяют явно указать this.
func.call(context, arg1, arg2, ...);
При этом вызывается функция func, первый аргумент call становится её this, а остальные передаются «как есть».
func.apply(context, [arg1, arg2]);
Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка.
function showFullName() { console.log(this.firstName + ' ' + this.lastName); } var user = { firstName: 'Василий', lastName: 'Иванов' }; // функция вызовется с this=user showFullName.call(user) // Василий Иванов
4. Вызов конструктора
Функции-конструкторы являются обычными функциями. Но есть два соглашения:
- Имя функции-конструктора должно начинаться с большой буквы
- Функция-конструктор должна вызываться при помощи оператора
new
function User(name) { this.name = name; this.isAdmin = false; } let user = new User('Вася'); alert(user.name); // Вася alert(user.isAdmin); // false
Когда функция вызывается как new User(…), происходит следующее:
- Создаётся новый пустой объект, и он присваивается
this - Выполняется код функции. Обычно он модифицирует
this, добавляя туда новые свойства - Возвращается значение
this
Другими словами, вызов new User(…) делает примерно вот что:
function User(name) { // this = {}; (неявно) // добавляет свойства к this this.name = name; this.isAdmin = false; // return this; (неявно) }
Вместо заключения
Мы уже видели пример потери this. Как только метод передаётся отдельно от объекта — this теряется.
var user = { surname: 'Иванов', show: function () { console.log('Фамилия', this.surname); } }; setTimeout(user.show, 1000); // Фамилия undefined
В современном JavaScript у функций есть встроенный метод bind, который позволяет зафиксировать this.
var user = { surname: 'Иванов', show: function () { console.log('Фамилия', this.surname); } }; user.show.bind(user) setTimeout(user.show, 1000); // Фамилия Иванов
Поиск: JavaScript • Web-разработка • Теория • Функция • this • bind • call • apply