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

От чего зависит контекст функции?

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

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

От чего зависит контекст функции

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

Основные правила определения контекста

1. Метод объекта — контекст объект

Если функция вызывается как метод объекта (через точку), this указывает на этот объект:

const user = {
  name: 'John',
  greet: function() {
    console.log(this.name); // this = user
  }
};

user.greet(); // Вывод: John

// Контекст зависит от объекта перед точкой
const person = {
  name: 'Alice',
  greet: user.greet // Та же функция
};

person.greet(); // Вывод: Alice (контекст = person)
user.greet();   // Вывод: John (контекст = user)

2. Обычный вызов функции — контекст undefined (strict mode) или window (non-strict)

const myFunc = function() {
  console.log(this);
};

myFunc(); // this = undefined (в strict mode) или window (браузер)

// В strict mode
function strictFunc() {
  'use strict';
  console.log(this); // undefined
}

strictFunc();

// В non-strict mode (браузер)
function nonStrictFunc() {
  console.log(this); // window (в браузере)
}

nonStrictFunc();

3. Конструктор (new) — контекст новый объект

function User(name) {
  this.name = name; // this = новый объект {}
}

const user = new User('Bob');
console.log(user.name); // Bob

// Объект создаётся и автоматически возвращается

4. .call(), .apply(), .bind() — контекст указываешь явно

function introduce(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

const person1 = { name: 'Alice' };
const person2 = { name: 'Bob' };

// .call() — вызов с явным контекстом
introduce.call(person1, 'Hello');      // Hello, Alice!
introduce.call(person2, 'Hi');         // Hi, Bob!

// .apply() — то же, но аргументы в массиве
introduce.apply(person1, ['Welcome']);  // Welcome, Alice!

// .bind() — создаёт новую функцию с привязанным контекстом
const greetAlice = introduce.bind(person1, 'Hey');
greetAlice(); // Hey, Alice!

5. Arrow функция — наследует контекст из окружения (лексический контекст)

const obj = {
  name: 'Object',
  regularFunc: function() {
    console.log('Regular:', this.name); // this = obj
    
    const arrow = () => {
      console.log('Arrow:', this.name);  // this наследуется из regularFunc
    };
    
    arrow(); // Arrow: Object
  },
  arrowFunc: () => {
    console.log('Arrow method:', this.name); // this = глобальный объект!
  }
};

obj.regularFunc(); // Regular: Object, Arrow: Object
obj.arrowFunc();   // Arrow method: undefined (this = window)

// Стрелка НЕ имеет своего this
const arrow = () => {
  console.log(this); // this из окружения (глобальный или родительский контекст)
};

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

Проблема: потеря контекста в обработчике события

const button = document.querySelector('button');
const handler = {
  name: 'Button Handler',
  
  // ❌ НЕПРАВИЛЬНО: this будет button (контекст события)
  onClick: function() {
    console.log(this.name); // undefined (this = button)
  },
  
  // ✅ ПРАВИЛЬНО 1: использовать bind
  onClick: function() {
    console.log(this.name);
  }.bind(handler), // Привязываем контекст явно
  
  // ✅ ПРАВИЛЬНО 2: стрелка функция
  onClick: () => {
    console.log(this.name); // Наследует контекст handler
  }
};

button.addEventListener('click', handler.onClick);

Проблема: setTimeout и контекст

const timer = {
  seconds: 10,
  start: function() {
    // ❌ НЕПРАВИЛЬНО
    setTimeout(function() {
      console.log(this.seconds); // undefined (this = window)
    }, 1000);
    
    // ✅ ПРАВИЛЬНО 1
    setTimeout(() => {
      console.log(this.seconds); // 10 (this = timer)
    }, 1000);
    
    // ✅ ПРАВИЛЬНО 2
    setTimeout(function() {
      console.log(this.seconds); // 10
    }.bind(this), 1000);
  }
};

timer.start();

Проблема: this в методах класса

class Counter {
  count = 0;
  
  // ❌ Проблема: контекст потеряется при передаче
  increment() {
    this.count++;
  }
  
  // ✅ Стрелка функция: контекст всегда на экземпляр
  incrementArrow = () => {
    this.count++;
  }
  
  // ✅ Или явно привязывать в конструкторе
  constructor() {
    this.increment = this.increment.bind(this);
  }
}

const counter = new Counter();
const btn = document.querySelector('button');

// С обычным методом - проблема
button.addEventListener('click', counter.increment); // this = button

// Со стрелкой - работает
button.addEventListener('click', counter.incrementArrow); // this = counter

Таблица определения контекста

СитуацияЗначение thisПример
Метод объектаСам объектobj.method() → this = obj
Обычный вызовundefined/windowfunc() → this = window
КонструкторНовый объектnew Func() → this = новый объект
.call(), .apply()Указанный объектfunc.call(obj) → this = obj
.bind()Привязанный объектfunc.bind(obj)() → this = obj
Стрелка функцияКонтекст снаружи() => {...} → this из окружения
Обработчик событияЭлемент событияel.addEventListener('click', fn) → this = el

Проверка контекста в консоли

function showContext() {
  console.log('this:', this);
  console.log('typeof this:', typeof this);
}

// Способ 1: метод объекта
const obj = { showContext };
obj.showContext(); // this = obj

// Способ 2: обычный вызов
showContext(); // this = window (браузер)

// Способ 3: явное указание
showContext.call({ myObj: true }); // this = { myObj: true }

Итог

Контекст функции (this) зависит от способа вызова:

  1. Метод объекта → контекст объект
  2. Простой вызов → контекст undefined/window
  3. Конструктор (new) → контекст новый объект
  4. call/apply/bind → контекст указываешь явно
  5. Стрелка функция → контекст из окружения
  6. Обработчик события → контекст элемент события

Основное правило: this определяется в момент вызова, не в момент объявления.