← Назад к вопросам

В каком случае функция может изменить свой контекст

1.0 Junior🔥 161 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Контекст функции (this) и способы его изменения

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

Что такое контекст (this)

const user = {
  name: "Иван",
  greet: function() {
    console.log("Привет, я " + this.name);
  }
};

user.greet(); // this = user, "Привет, я Иван"

В этом примере this указывает на объект user.

Способы изменения контекста

1. Метод call() — вызов с явным контекстом

const user1 = { name: "Иван" };
const user2 = { name: "Мария" };

function greet(greeting) {
  console.log(greeting + ", я " + this.name);
}

// call() передает контекст первым аргументом
greet.call(user1, "Привет"); // "Привет, я Иван"
greet.call(user2, "Привет"); // "Привет, я Мария"

Синтаксис:

function.call(context, arg1, arg2, ...);

2. Метод apply() — похож на call(), но аргументы в массиве

const user = { name: "Иван" };

function introduce(age, city) {
  console.log(${this.name}, мне ${age}, живу в ${city}`);
}

// apply() передает аргументы массивом
introduce.apply(user, [25, "Москва"]);
// "Я Иван, мне 25, живу в Москва"

Синтаксис:

function.apply(context, [arg1, arg2, ...]);

3. Метод bind() — создает новую функцию с привязанным контекстом

const user = { name: "Иван" };

function greet(greeting) {
  console.log(greeting + ", я " + this.name);
}

// bind() возвращает новую функцию с привязанным контекстом
const greetIvan = greet.bind(user, "Привет");

greetIvan(); // "Привет, я Иван"
greetIvan(); // "Привет, я Иван" (контекст всегда привязан)

Синтаксис:

const boundFunction = function.bind(context, arg1, arg2, ...);

4. Стрелочные функции — наследуют контекст от окружающей области

const user = {
  name: "Иван",
  greet: function() {
    console.log("Контекст function:", this.name); // Иван
    
    // Стрелочная функция наследует this от greet
    const arrow = () => {
      console.log("Контекст стрелочной:", this.name); // Иван
    };
    
    arrow();
  }
};

user.greet();

Важно: Стрелочная функция не имеет собственного this, она использует this родительского scope.

Сравнение способов

call() vs apply() vs bind()

const calculator = {
  value: 10
};

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

// call() — аргументы через запятую, вызывает сразу
const result1 = add.call(calculator, 5, 3); // 18

// apply() — аргументы массивом, вызывает сразу
const result2 = add.apply(calculator, [5, 3]); // 18

// bind() — возвращает новую функцию
const boundAdd = add.bind(calculator);
const result3 = boundAdd(5, 3); // 18
МетодАргументыВызовВозвращает
call()Через запятуюСразуРезультат функции
apply()МассивомСразуРезультат функции
bind()Через запятуюНет (возвращает функцию)Новую функцию

Практические примеры

Пример 1: Привязка обработчика события

class Button {
  constructor(selector) {
    this.element = document.querySelector(selector);
    this.clickCount = 0;
    
    // Нужно использовать bind, чтобы this указывал на класс
    this.element.addEventListener("click", this.handleClick.bind(this));
  }
  
  handleClick() {
    this.clickCount++;
    console.log(`Кликнули ${this.clickCount} раз`);
  }
}

const btn = new Button(".btn");

Пример 2: Использование apply с Math

const numbers = [5, 10, 25, 3, 100];

// apply хорош для передачи массива как аргументов
const max = Math.max.apply(null, numbers); // 100
const min = Math.min.apply(null, numbers); // 3

// или с современным синтаксисом spread:
const max2 = Math.max(...numbers); // 100

Пример 3: Конструирование объектов (constructor stealing)

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Employee(name, age, salary) {
  // Используем call, чтобы унаследовать свойства Person
  Person.call(this, name, age);
  this.salary = salary;
}

const emp = new Employee("Иван", 25, 100000);
console.log(emp); // { name: "Иван", age: 25, salary: 100000 }

Пример 4: Правильная привязка в React (функциональные компоненты)

// НЕПРАВИЛЬНО - контекст теряется
class Counter {
  state = { count: 0 };
  
  // Этот подход уже устарел (используйте Function Components + Hooks)
  increment() {
    this.state.count++;
  }
  
  render() {
    return <button onClick={this.increment}>
      Счет: {this.state.count}
    </button>;
  }
}

// ПРАВИЛЬНО - используйте bind или стрелочную функцию
class Counter {
  constructor() {
    this.increment = this.increment.bind(this);
  }
  
  increment() {
    this.state.count++;
  }
}

// или стрелочная функция (этот синтаксис лучше):
class Counter {
  increment = () => {
    this.state.count++;
  }
}

Контекст в разных сценариях

const user = {
  name: "Иван",
  sayHello: function() {
    console.log("Привет от " + this.name);
  }
};

// Сценарий 1: Вызов как метод объекта
user.sayHello(); // this = user

// Сценарий 2: Вызов как обычная функция
const fn = user.sayHello;
fn(); // this = undefined (strict mode) или window

// Сценарий 3: Вызов с call()
user.sayHello.call({ name: "Мария" }); // this = { name: "Мария" }

// Сценарий 4: Вызов с bind()
const boundFn = user.sayHello.bind({ name: "Петр" });
boundFn(); // this = { name: "Петр" }

// Сценарий 5: Вызов в стрелочной функции
const arrow = () => user.sayHello(); // используется this из arrow scope

Когда контекст меняется автоматически

1. При присваивании функции переменной

const user = { name: "Иван" };
const fn = user.sayHello; // контекст потеряется
fn(); // this будет undefined (или window)

2. При передаче функции как callback

const btn = document.querySelector("button");
const user = { name: "Иван", greet: function() { ... } };

btn.addEventListener("click", user.greet); // контекст потеряется
// нужно: btn.addEventListener("click", user.greet.bind(user));

3. При использовании в setTimeout/setInterval

const counter = {
  count: 0,
  increment: function() {
    this.count++;
  }
};

setTimeout(counter.increment, 1000); // контекст потеряется
// нужно: setTimeout(counter.increment.bind(counter), 1000);

Ключевые выводы

  1. call() и apply() вызывают функцию с новым контекстом сразу
  2. bind() возвращает новую функцию с привязанным контекстом
  3. Стрелочные функции наследуют контекст от родительского scope
  4. Контекст теряется при присваивании функции переменной или передаче как callback
  5. Привязка контекста — критична при работе с обработчиками событий и callback функциями
В каком случае функция может изменить свой контекст | PrepBro