# this, call, apply, bind

[![Generic badge](https://img.shields.io/badge/JavaScript-%D0%B4%D0%BB%D1%8F%20%D1%82%D1%83%D0%BF%D1%8B%D1%85-green.svg)](https://img.shields.io/badge/JavaScript-%D0%B4%D0%BB%D1%8F%20%D1%82%D1%83%D0%BF%D1%8B%D1%85-green.svg)

![](/files/-M8WRgeMRvp4rjrgvyrZ)

В этом уроке расскажу о таких надуманно сложных вещах, как `call`, `bind`, `apply`, ну и, соответственно, затрону `this`. Все эти слова связаны одним словом – **контекст**. Но, обо всем по порядку.

Многие боятся даже подходить к этой теме, потому как им кажется, что это что-то невероятно сложная и непонятная тема для новичков. Поэтому, надеюсь, что сегодня я развенчаю все ваши страхи и непонятки по этой теме.

Все примеры будут сделаны на основе **функций**, поэтому нужно представлять как они работают.

Предыдущие уроки по функциям: [Функции](/javascript-for-dummies/funkcii.md), [Функции. Возврат значения](/javascript-for-dummies/funkcii.-vozvrat-znacheniya.md)

### **Ключевое слово this** <a href="#toc_1" id="toc_1"></a>

Создадим обычную **функцию** и сразу же вызовем её:

```javascript
function hi() {
    console.log('Привет', this);
}

hi();
```

Функция выводит в консоль слово `Привет` и `this`. Если заглянуть в консоль, то увидим следующее:

![](https://teletype.in/files/3f/a6/3fa67ec3-9aa4-4681-9af2-dd6e140d3db1.png)

Если со словом `Привет` все понятно, то вот что это такое вывелось на месте `this`? Почему вывелся какой-то объект `Window`?

Когда мы создаем глобальные объекты, то они автоматически начинают принадлежать глобальному объекту `window` – самому главному объекту в браузере.

На самом деле все методы, которые ты часто вызываешь, например: `console.log()`, `alert`, `prompt` и т.д. находятся внутри объекта `window` и являются его **методами**.

Это достаточно просто проверить. Просто попробуй вызвать эти методы  следующим образом:

* `window.console.log('Привет');`
* `window.alert('Привет');`
* `window.prompt('Как тебя зовут?');`

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

```javascript
function hi() {
    console.log('Привет', this);
}

hi();
```

На самом деле, когда мы вызываем функцию, то мы вызываем её так же из объекта `window`, т.е. вызов нашей функции можно переписать так:

```javascript
window.hi();
```

Из всего выше сказанного можно сделать вывод, что наша **функция** запускается из **объекта** `window`, т.е. **контекст**, в котором выполняется наша **функция** так же равна **объекту** `window`. А ключевое слово `this` как раз и содержит внутри себя **контекст**, в котором вызывается **функция/метод**.

А теперь создадим **объект**:

```javascript
const man = {
   name: 'Вася',
   lastName: 'Пупкин',
   age: 30,
   sayHi: hi
};
```

Простой объект описывающий абстрактного мужчину. Обрати внимание на метод `sayHi`. В него мы передали ссылку на **функцию**, которую создали в самом начале.

Давай вызовем метод `sayHi` из нашего **объекта**:

```javascript
man.sayHi();
```

А теперь посмотрим в консоль, что же теперь вывелось на месте `this`. Получаем следующий результат:

![](https://teletype.in/files/97/75/97758a5c-de07-4010-bc09-0646d7f2f5c6.png)

В этом случае значение `this` (**контекста**) становится равным самому **объекту**. Почему так случилось? Все очень просто.

Первый раз мы вызывали **функцию** `hi` и **значение** `this`, внутри неё было равно **объекту** `Window`, потому что мы запускали **функцию** в **контексте объекта** `Window`.

А сейчас, мы создали свой объект, и запустили функцию `hi` из него. Получается, что место вызова функции изменилось с объекта `Window` на объект `man`. Следовательно, изменилось и значение this **с** `Window` **на** `man`.

**Теперь коротко:** *значение `this` равно тому* объекту в контексте которого было вызвано.

С контекстом разобрались, давай разбираться с этими страшными методами: `call`, `apply`, `bind`.

### **Метод bind** <a href="#toc_2" id="toc_2"></a>

Мы ранее создали объект `man`, а теперь еще создадим объект `woman`. Итого, получим:

```javascript
const man = {
   name: 'Вася',
   lastName: 'Пупкин',
   age: 30,
   sayHi: hi
};

const woman = {
   name: 'Катя',
   lastName: 'Иванова',
   age: 25
};
```

Итак, мы имеем **2 объекта**, один из которых описывает *мужчину*, а второй *женщину*.

Для примера, создадим еще один объект (**логгер**), которому добавим один **метод**:

```javascript
const Logger = {
   info: function() {
       console.log('Имя: ', this.name);
       console.log('Фамилия: ', this.lastName);
       console.log('Возраст: ', this.age);
   }
};
```

Внутри объекта `Logger` мы создали метод `info`, который должен выводить информацию о наших объектах. Мы использовали ключевое слово `this` внутри этого метода:

```javascript
console.log('Имя: ', this.name);
console.log('Фамилия: ', this.lastName);
console.log('Возраст: ', this.age);
```

Но что будет, если вызвать этот метод прямо сейчас?

```javascript
Logger.info();
```

Результат:

![](https://teletype.in/files/7d/5d/7d5da565-4ccd-4e66-b48c-dbeeb9cc9162.png)

Мы получили `undefined` во всех случаях и это более чем логично. Т.к. мы обращаемся к ключевому слову `this` внутри метода `info`, который принадлежит объекту `Logger`, то и контекст, т.е. само ключевое `this` имеет значение, которое равно объекту `Logger`. А внутри этого объекта у нас имеется только один метод `info`. Никаких свойств с именами `name`, `lastName`, `age` у нас нет.

Как сделать так, чтобы с помощью объекта `Logger` и его метода `info` вывести данные, например, об объекте `man`?

Как раз здесь на помощь приходит тот самый страшный метод `bind`:

```javascript
const loggerMan = Logger.info.bind(man);
```

У каждой функции в **JavaScript** существует метод `bind`, благодаря которому мы можем любой **функции** задать **контекст** внутри которого она должна будет выполняться.

Метод `bind` возвращает новую функцию к которой будет привязан тот контекст, который мы указали как аргумент метода `bind`. В нашем случае контекстом мы указали объект `man`. Так как `bind` возвращает новую функцию, то мы её запишем в константу `loggerMan`.

Теперь вызовем полученную функцию:

```javascript
loggerMan();
```

Результат:

![](https://teletype.in/files/f7/b2/f7b23b45-ce09-4f70-948a-77fee522f7e9.png)

Как видишь, `bind` успешно **привязал** в качестве **контекста объект** `man` и информация о нём успешно вывелась в консоль.

Теперь мы хотим получить информацию об объекте `woman` с помощью нашего `логгера`. Для этого делаем то же самое:

```javascript
const loggerWoman = Logger.info.bind(woman);
```

С помощью `bind` мы **привязываем** новый **контекст** для **метода** `info` со **значением**`woman`.

Вызовем:

```javascript
loggerWoman();
```

Результат:

![](https://teletype.in/files/f0/be/f0be6b29-ffe2-430f-8d51-15182d094da4.png)

Вуаля, все так же прекрасно работает. И ведь совсем не сложно и не страшно, верно?

И последнее, что хотелось бы здесь отметить. В функцию `info` мы можем дополнительно передавать какие-либо параметры, если это нужно. Давай немного исправим метод `info` в объекте `Logger`. Пускай он будет принимать **пол человека**:

```javascript
const Logger = {
   info: function(sex) {
       console.log('Имя: ', this.name);
       console.log('Фамилия: ', this.lastName);
       console.log('Возраст: ', this.age);
       console.log('Пол: ', sex);
   }
};
```

Все, что мы сделали -- добавили в определение функции параметр `sex` и вывели его под остальными данными. Остается вопрос, как нам туда это все передать, если нам еще и контекст нужно сменить?! Все очень просто. У нас уже есть полученные функции для наших объектов:

```javascript
const loggerMan = Logger.info.bind(man);
const loggerWoman = Logger.info.bind(woman);
```

Чтобы передать параметр `sex`, нам достаточно указать его аргументом наших функций при их вызове:

```javascript
loggerMan('мужской');
loggerWoman('женский');
```

Смотрим на результат:

![](https://teletype.in/files/54/a0/54a0ba8e-0ea5-4ed7-a6aa-a9cdde06a889.png)

![](https://teletype.in/files/15/d7/15d78836-28f5-4454-a2f3-9380f0aeb016.png)

Все прекрасно отработало. Но это не единственный способ передачи параметров.

Есть **еще один способ передачи параметров** – указать их при вызове метода `bind`.

Получим следующее:

```javascript
const loggerMan = Logger.info.bind(man, 'мужской');
const loggerWoman = Logger.info.bind(woman, 'женский');

loggerMan();
loggerWoman();
```

Результат:

![](https://teletype.in/files/09/bf/09bfc854-4775-4c21-9d99-c3a7eaf81a61.png)

![](https://teletype.in/files/39/1d/391d25dc-48ff-4429-8550-d538ceeb20a9.png)

Результат никак не поменялся и все прекрасно продолжает работать.

Первый аргумент в методе `bind` – это сам **контекст**, который нужно **привязать** к **функции**. А вот дальше, через **запятую**, можно передавать сколько угодно дополнительных **параметров (аргументов)**, которые должны попасть в метод `info`. В нашем случае нам нужно было передать только один аргумент `sex`.

На этом с `bind` всё. Не такой уж и страшный зверь, если приглядеться.

### **Метод call** <a href="#toc_3" id="toc_3"></a>

Что ж, продолжим убивать страх внутри тебя. Теперь на очереди метод `call`.

Ты не поверишь, но он делает то же самое, что и `bind`. С одной маленькой **поправкой**: если метод `bind` привязывает контекст и возвращает новую функцию с этим контекстом, то метод `call` привязывая контекст, сразу же вызывает указанную функцию, а не возвращает новую.

Продублирую наш код:

```javascript
const Logger = {
   info: function(sex) {
       console.log('Имя: ', this.name);
       console.log('Фамилия: ', this.lastName);
       console.log('Возраст: ', this.age);
       console.log('Пол: ', sex);
   }
};

const man = {
   name: 'Вася',
   lastName: 'Пупкин',
   age: 30,
   sayHi: hi
};

const woman = {
   name: 'Катя',
   lastName: 'Иванова',
   age: 25
};

const loggerMan = Logger.info.bind(man, 'мужской');
const loggerWoman = Logger.info.bind(woman, 'женский');

loggerMan();
loggerWoman();
```

Итак, давай **привяжем контекст** с помощью `call`, а не `bind` и посмотрим в чем же разница.

**Было**:

```javascript
const loggerMan = Logger.info.bind(man, 'мужской');
const loggerWoman = Logger.info.bind(woman, 'женский');
```

**Стало**:

```javascript
Logger.info.call(man, 'мужской');
Logger.info.call(woman, 'женский');
```

Так как метод `call` после привязки контекста сразу же выполняет функцию (`info`), и наша функция ничего не возвращает (внутри нет ключевого слова `return`), то не имеет никакого смысла записывать результат данной функции, так как он всегда в нашем случае будет равен `undefined`, поэтому создание констант `loggerMan` и `loggerWoman` делать не нужно.

Вот так вот просто. `call` – ничуть не страшнее `bind`. Вся разница лишь в том, что:

* `bind` **привязывает контекст и возвращает новую функцию** и мы можем вызвать её в любом месте;
* `call` **привязывает контекст и сразу же вызывает функцию**

Собственно, больше о `call` и сказать нечего.

### **Метод apply** <a href="#toc_4" id="toc_4"></a>

Ты должно быть заметил, что о методе `bind` было сказано очень много. О методе `call` – уже куда меньше, потому что это одно и тоже с одним исключением. Будешь смеяться, но метод `apply` – это то же самое, что и `call`. И существует только **одно отличие**.

Если метод `call` (как, собственно, и `bind`) первым аргументом принимает **контекст**, а дальше через **запятую принимает аргументы для функции** (получается, что аргументов может быть разное количество), то метод `apply` принимает всего **2 аргумента**:

* Контекст (так же как `bind` и `call`);
* Массив параметров.

Т.е. если в `bind` и `call` мы можем **передавать параметры функции через запятую**, то в `apply` мы должны **передать их в массиве**. И в этом вся разница. Давай перепишем код с `call` на `apply`.

**Было**:

```javascript
Logger.info.call(man, 'мужской');
Logger.info.call(woman, 'женский');
```

**Стало**:

```javascript
Logger.info.apply(man, ['мужской']);
Logger.info.apply(woman, ['женский']);
```

Вот собственно и вся разница. В случае с `call` мы передали аргумент `sex` для функции `info`как обычную строку. А в случае с `apply` мы обязаны передавать все аргументы в массиве.

Надеюсь, теперь ты поборол свой страх и понял, что всё, что связано с **контекстами**, конкретно, **данными методами** – совсем не страшно.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://thedvlpr.gitbook.io/javascript-for-dummies/this-call-apply-bind.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
