JavaScript. Контекст вызова функции

17.06.2021

Теги: JavaScriptWeb-разработкаТеорияФункция

В JavaScript this — это текущий контекст исполнения функции, он определяется в момент вызова. Функцию можно вызвать четырьмя способами и каждый из них определяет свой контекст. Кроме того, режим strict также влияет на контекст исполнения. Рассмотрим каждый способ вызова функции и посмотрим, на что будет указывать this в каждом из них.

  1. Простой вызов функции — hello('Привет')
  2. Вызов метода объекта — user.hello('Привет')
  3. Вызов с указанием контекста — hello.call(user, 'Привет')
  4. Вызов конструктора — 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

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.