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

Как работает ключевое слово this?

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

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

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

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

Как работает ключевое слово this в JavaScript

Основная концепция

this — это контекстная переменная, которая ссылается на объект, в контексте которого выполняется код. Главное правило: значение this определяется НЕ местом написания функции, а способом её вызова. Это одна из самых запутанных частей JavaScript, поэтому разберу её детально.

Правило 1: Вызов как метод объекта

Если функция вызывается как obj.method(), то this внутри функции равен obj:

const person = {
  name: 'Alice',
  age: 30,
  greet() {
    console.log(`Hello, I am ${this.name}`);
    console.log(`I am ${this.age} years old`);
  },
};

person.greet();
// Вывод:
// Hello, I am Alice
// I am 30 years old

// Объект СЛЕВА от точки — это this!

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

Если сохранить метод в переменную и вызвать его отдельно, контекст теряется:

const person = {
  name: 'Bob',
  greet() {
    console.log(`Hello, ${this.name}`);
  },
};

// Сохраняю метод в переменную
const greetFunction = person.greet;

// Вызываю как обычную функцию (не через person.greet)
greetFunction(); // Hello, undefined

// this здесь равен undefined (в strict mode) или window/global (в обычном режиме)

Правило 3: Вложенные объекты

Что происходит с this в методах вложенных объектов?

const user = {
  name: 'Charlie',
  profile: {
    title: 'Developer',
    display() {
      console.log(this.title);
      console.log(this.name); // undefined!
    },
  },
};

user.profile.display();
// Вывод:
// Developer
// undefined

// this === profile (объект НЕПОСРЕДСТВЕННО слева от точки)
// НЕ this === user

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

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

function User(name) {
  this.name = name;
  this.greet = function() {
    console.log(`Hi, ${this.name}`);
  };
}

const alice = new User('Alice');
alice.greet(); // Hi, Alice

// this === новый объект, созданный new

const bob = new User('Bob');
bob.greet(); // Hi, Bob

// Каждый new создаёт НОВЫЙ объект с новым this

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

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

function introduce() {
  console.log(`My name is ${this.name}`);
}

const person = { name: 'Diana' };
const animal = { name: 'Dog' };

// call - вызывает функцию с указанным this
introduce.call(person); // My name is Diana
introduce.call(animal);  // My name is Dog

// apply - то же, что call, но параметры через массив
function greet(greeting) {
  console.log(`${greeting}, ${this.name}`);
}

greet.apply(person, ['Hello']); // Hello, Diana

// bind - создаёт НОВУЮ функцию с привязанным this
const boundGreet = greet.bind(person);
boundGreet('Hi'); // Hi, Diana

// bind не вызывает функцию, а возвращает новую функцию
const greetDiana = introduce.bind(person);
setTimeout(greetDiana, 1000); // Через 1 сек: My name is Diana

Правило 6: Стрелочные функции (лексический this)

Стрелочные функции НЕ имеют своего this. Они используют this из окружающего контекста:

const obj = {
  name: 'Eve',
  
  // Обычная функция имеет свой this
  regularMethod: function() {
    console.log('regular:', this.name); // Eve
    
    // Функция в функции
    function nested() {
      console.log('nested:', this.name); // undefined!
    }
    nested();
    
    // Стрелочная функция наследует this
    const arrow = () => {
      console.log('arrow:', this.name); // Eve
    };
    arrow();
  },
  
  // Стрелочная функция как метод
  arrowMethod: () => {
    console.log('arrow method:', this.name); // undefined
    // this — это объект, в котором определён сам obj
    // (обычно global/window)
  },
};

obj.regularMethod();
obj.arrowMethod();

Практический пример: React класс vs hooks

Классовый компонент (с this)

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    
    // Нужен bind, чтобы this работал в методе
    this.increment = this.increment.bind(this);
  }

  increment() {
    // this === компонент
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>+1</button>
      </div>
    );
  }
}

Или с стрелочной функцией (no bind needed)

class Counter extends React.Component {
  state = { count: 0 };

  // Стрелочная функция наследует this
  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <button onClick={this.increment}>+1</button>
      </div>
    );
  }
}

Функциональный компонент (no this)

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

Практический алгоритм определения this

Шаг 1: Посмотри, как функция вызывается

Вызов типа 1: obj.method()

  • this = obj

Вызов типа 2: new Function()

  • this = новый объект

Вызов типа 3: func.call(obj) или func.apply(obj) или func.bind(obj)

  • this = obj

Вызов типа 4: => стрелочная функция

  • this = this из окружающего кода

Вызов типа 5: обычная функция без контекста

  • this = undefined (strict mode) или window/global

Распространённые ошибки

// Ошибка 1: Обработчик события теряет контекст
class Component {
  name = 'MyComponent';
  
  handleClick() {
    console.log(this.name); // undefined!
  }
  
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

// Решение 1: использовать стрелочную функцию
class Component {
  name = 'MyComponent';
  handleClick = () => {
    console.log(this.name); // MyComponent
  };
}

// Решение 2: привязать в конструкторе
class Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this.name);
  }
}

Итоговый принцип

this не определяется в момент НАПИСАНИЯ функции, а определяется в момент ЕЕ ВЫЗОВА. Всегда спрашивай себя: 'Кто вызвал эту функцию? Что находится слева от точки?' Ответ — это this. Если функция вызвана без контекста или это стрелочная функция — смотри окружающий код.