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

Что такое this?

1.3 Junior🔥 301 комментариев
#JavaScript Core

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

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

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

# Что такое this?

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

Краткое определение

this это объект, который "принадлежит" коду при его выполнении. Его значение определяется в момент вызова функции, а не в момент её определения.

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

user.greet(); // Hello, John

Правила определения this

1. Метод объекта (Method invocation)

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

const person = {
  name: 'Alice',
  age: 30,
  sayName() {
    console.log(this.name); // this = person
  },
  birthday() {
    this.age++; // Изменяем свойство person
  }
};

person.sayName();   // Alice
person.birthday();  // Увеличиваем age

2. Простой вызов функции (Function call)

При простом вызове функции this указывает на глобальный объект (window в браузере, global в Node.js) или undefined в strict mode:

function sayHello() {
  console.log(this); // undefined в strict mode, window в обычном
}

sayHello();

// В браузере (обычный режим)
console.log(this); // Window { window: Window, ... }

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

3. Конструктор (new)

При вызове функции с new, this указывает на новый объект:

function Person(name) {
  this.name = name; // this = новый объект
  this.greet = function() {
    console.log(`Hi, I'm ${this.name}`);
  };
}

const john = new Person('John');
john.greet(); // Hi, I'm John
console.log(john.name); // John

4. Явное указание контекста (call, apply, bind)

Mожно явно указать значение this используя методы call, apply или bind:

const greeting = function() {
  console.log(`Hello, ${this.name}`);
};

const alice = { name: 'Alice' };
const bob = { name: 'Bob' };

// call — передаём this как первый аргумент
greeting.call(alice);  // Hello, Alice
greeting.call(bob);    // Hello, Bob

// apply — подобно call, но принимает массив аргументов
const greetWithAge = function(age) {
  console.log(`Hello, ${this.name}, age ${age}`);
};

greetWithAge.apply(alice, [25]); // Hello, Alice, age 25

// bind — создаёт новую функцию с привязанным контекстом
const boundGreeting = greeting.bind(alice);
boundGreeting(); // Hello, Alice (вне зависимости от контекста вызова)

Примеры проблем с this

Проблема 1: потеря контекста

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

// Когда передаём метод как callback, теряем контекст
const greetFunc = person.greet;
greetFunc(); // undefined (или ошибка в strict mode)

// Решение 1: использовать стрелочную функцию
const person2 = {
  name: 'John',
  greet: () => {
    console.log(this.name); // ❌ стрелочная функция наследует this снаружи
  },
  greetProper() {
    // ✅ Правильно для методов
    console.log(this.name);
  }
};

// Решение 2: использовать bind
const greetBound = person.greet.bind(person);
greetBound(); // John

// Решение 3: использовать стрелочную функцию как property
const person3 = {
  name: 'John',
  greet: () => {
    // ❌ Неправильно — стрелочная функция не имеет своего this
  }
};

Проблема 2: this в Event Listeners

const button = document.querySelector('button');
const handler = {
  clicks: 0,
  handleClick() {
    // ❌ ПРОБЛЕМА: this = button, а не handler
    this.clicks++; // Добавляет свойство на button, а не handler
  }
};

button.addEventListener('click', handler.handleClick); // Потеря контекста

// Решение 1: bind
button.addEventListener('click', handler.handleClick.bind(handler));

// Решение 2: стрелочная функция
button.addEventListener('click', () => handler.handleClick());

// Решение 3: стрелочная функция как property
const handler2 = {
  clicks: 0,
  handleClick: () => {
    // ❌ Стрелочная функция наследует this из внешней области
    this.clicks++;
  }
};

Проблема 3: this в setTimeout

const person = {
  name: 'John',
  showNameLater() {
    // ❌ this = undefined (в strict mode) или window
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 1000);
  },
  
  // ✅ Решение: стрелочная функция
  showNameLaterFixed() {
    setTimeout(() => {
      console.log(this.name); // this наследуется из showNameLaterFixed
    }, 1000);
  },
  
  // Альтернатива: bind
  showNameLaterBind() {
    setTimeout(function() {
      console.log(this.name);
    }.bind(this), 1000);
  }
};

person.showNameLaterFixed(); // John (после 1 секунды)

this в классах

class Person {
  constructor(name) {
    this.name = name;
  }
  
  // Метод: this = экземпляр класса
  greet() {
    console.log(`Hello, ${this.name}`);
  }
  
  // Стрелочная функция как property: this = экземпляр класса
  greetArrow = () => {
    console.log(`Hello, ${this.name}`);
  }
}

const john = new Person('John');
john.greet();       // Hello, John
john.greetArrow();  // Hello, John

// Проблема с методами
const greetFunc = john.greet;
greetFunc(); // ❌ undefined

// Стрелочные функции как properties решают проблему
const greetArrowFunc = john.greetArrow;
greetArrowFunc(); // ✅ Hello, John

this vs стрелочные функции

const obj = {
  name: 'Object',
  
  // ✅ Обычная функция: this = obj
  regularMethod() {
    console.log(this.name); // 'Object'
  },
  
  // ❌ Стрелочная функция: this = внешний контекст
  arrowMethod: () => {
    console.log(this.name); // undefined
  },
  
  // ✅ Правильно для callback'ов
  handleCallback: function() {
    setTimeout(() => {
      // Стрелочная функция наследует this из handleCallback
      console.log(this.name); // 'Object'
    }, 100);
  }
};

obj.regularMethod();  // Object
obj.arrowMethod();    // undefined
obj.handleCallback(); // Object (после 100ms)

Таблица: когда какой this

Контекстthis указывает на
Метод объектаОбъект
Простой вызовundefined (strict) или window
new ConstructorНовый объект
.call(obj)obj
.apply(obj)obj
.bind(obj)obj
Стрелочная функцияВнешний this (лексический)
Event listenerЭлемент (addEventListener)

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

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.count = 0;
  }
  
  // ❌ Проблема: this может быть undefined
  increment() {
    this.count++; // this может быть undefined
  }
  
  // Решение 1: bind в конструкторе
  incrementBound() {
    this.count++;
  }
  
  // Решение 2: стрелочная функция как property
  incrementArrow = () => {
    this.count++;
  }
  
  render() {
    return (
      <div>
        <button onClick={this.incrementArrow}>Increment</button>
      </div>
    );
  }
}

// В функциональных компонентах этой проблемы нет
function CounterFunc() {
  const [count, setCount] = React.useState(0);
  
  const increment = () => {
    setCount(count + 1);
  };
  
  return <button onClick={increment}>Increment</button>;
}

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

  1. this определяется при вызове функции, а не при её определении
  2. В методах объекта this указывает на объект
  3. При простом вызове this неопределён (strict mode) или window
  4. С new this указывает на новый объект
  5. call, apply, bind позволяют явно указать контекст
  6. Стрелочные функции наследуют this из внешней области
  7. В современном коде часто используются стрелочные функции чтобы избежать проблем с this
Что такое this? | PrepBro