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

Как вычисляется This?

2.0 Middle🔥 211 комментариев
#JavaScript Core

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

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

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

Вычисление This в JavaScript: полное руководство

this — одна из самых запутанных концепций в JavaScript. Её значение динамически вычисляется в момент вызова функции, а не при её определении. Давайте разберёмся в правилах вычисления.

Правило 1: Глобальный контекст (Global Context)

Если функция вызывается в глобальном контексте, this указывает на глобальный объект:

function greet() {
  console.log(this);
}

greet();  // this === window (в браузере) или global (в Node.js)

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

Правило 2: Вызов как метод объекта (Method Call)

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

const user = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, ${this.name}`);
  }
};

user.greet();  // this === user → "Hello, Alice"

// Но если присвоить метод переменной — контекст теряется
const sayHello = user.greet;
sayHello();  // this === window (или undefined в strict) → "Hello, undefined"

Почему происходит потеря контекста? Потому что функция вызывается без объекта-хозяина. Решение — использовать bind:

const sayHello = user.greet.bind(user);
sayHello();  // this === user → "Hello, Alice"

Правило 3: Явное связывание с call, apply, bind

call — вызывает функцию с явным this:

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

const user = { name: 'Bob' };
greet.call(user, 'Hello');  // this === user → "Hello, Bob"

apply — аналогично call, но аргументы передаются массивом:

greet.apply(user, ['Hi']);  // this === user → "Hi, Bob"

// Практическое применение — найти максимум
const numbers = [5, 2, 8, 1];
const max = Math.max.apply(null, numbers);  // 8

bind — создаёт новую функцию с привязанным this:

const sayHello = greet.bind(user);
sayHello('Hey');  // this === user → "Hey, Bob"
sayHello('Yo');   // this === user → "Yo, Bob"
// bind возвращает функцию, которая ВСЕГДА имеет this === user

Правило 4: Конструктор (Constructor)

Если функция вызывается с new, this указывает на новый созданный объект:

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

const alice = new User('Alice');
console.log(alice.name);  // "Alice"
console.log(this === alice);  // true (внутри конструктора)

Правило 5: Стрелочные функции (Arrow Functions)

Стрелочные функции НЕ имеют своего this! Они наследуют this из внешней области видимости (lexical this):

const user = {
  name: 'Charlie',
  greet: function() {
    console.log(this.name);  // "Charlie" (обычная функция)
  },
  greetArrow: () => {
    console.log(this.name);  // undefined (стрелочная функция наследует this с уровня выше)
  },
  delayedGreet: function() {
    const arrow = () => {
      console.log(this.name);  // "Charlie" (наследует this из delayedGreet)
    };
    setTimeout(arrow, 1000);  // this остаётся Charlie
  },
  delayedGreetNormal: function() {
    function inner() {
      console.log(this.name);  // undefined — потеря контекста!
    }
    setTimeout(inner, 1000);
  }
};

user.greet();              // "Charlie"
user.greetArrow();         // undefined
user.delayedGreet();       // "Charlie" (через 1 сек)
user.delayedGreetNormal(); // undefined (через 1 сек)

Стрелочные функции идеальны для:

  • Callbacks в setTimeout/setInterval
  • Array методов (map, filter, forEach)
  • React class component методов

Правило 6: Приоритет (Порядок применения)

Когда несколько правил применяются одновременно, вот приоритет:

  1. Стрелочная функция → наследует this из контекста
  2. bind/call/apply → явное связывание
  3. new → конструктор создаёт новый объект
  4. Метод объектаthis === объект
  5. Глобальный контекстwindow или undefined
const obj = {
  value: 42,
  getValue: function() {
    return this.value;
  }
};

const boundGetValue = obj.getValue.bind({ value: 100 });
console.log(boundGetValue());  // 100 — bind имеет приоритет над методом!

Практический пример: проблема в React классах

// Плохо — потеря контекста
class Button extends React.Component {
  handleClick() {
    console.log(this.props);  // undefined!
  }
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

// Решение 1 — bind в конструкторе
class Button extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this.props);
  }
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

// Решение 2 — стрелочная функция (modern approach)
class Button extends React.Component {
  handleClick = () => {
    console.log(this.props);
  }
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

Валидация this — критически важна для понимания JavaScript. Помни: контекст определяется в момент вызова, а не в момент определения функции (кроме стрелочных функций).