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

Вызывается ли watchEffect только при изменении

2.0 Middle🔥 171 комментариев
#Vue.js

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

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

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

Как работает this в JavaScript

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

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

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

const user = {
  name: 'Иван',
  greet() {
    console.log(`Hello, ${this.name}`);
  }
};

user.greet(); // 'Hello, Иван' (this === user)

const greet = user.greet;
greet(); // 'Hello, undefined' (this === window или undefined)

Почему меняется?

// Когда вызовешь как метод объекта
user.greet(); // this = user

// Когда вызовешь как функцию
const fn = user.greet;
fn(); // this = undefined (strict mode) или window

// Хотя это один и тот же код функции!
// Важен СПОСОБ вызова, не объявления

Правило 2: Обычная функция

В обычной функции this зависит от режима (strict или нет).

// Нестрогий режим
function test() {
  console.log(this);
}

test(); // window (браузер) или global (Node.js)

// Строгий режим
'use strict';
function test() {
  console.log(this);
}

test(); // undefined

Правило 3: Стрелочная функция (arrow function)

Стрелочная функция НЕ имеет своего this. Она берет this из окружающей области (lexical this).

const user = {
  name: 'Иван',
  greet: () => {
    console.log(this.name); // this из окружения, НЕ user!
  }
};

user.greet(); // undefined (this из глобальной области)

// Правильно:
const user2 = {
  name: 'Петр',
  greet() {
    const sayName = () => {
      console.log(this.name); // Берет this из greet
    };
    sayName();
  }
};

user2.greet(); // 'Петр'

Стрелочные функции в методах — плохая идея

const obj = {
  value: 42,
  getValue: () => {
    return this.value; // this = window/global, не obj
  }
};

console.log(obj.getValue()); // undefined

// Используй обычные функции для методов
const obj2 = {
  value: 42,
  getValue() {
    return this.value; // this = obj2
  }
};

console.log(obj2.getValue()); // 42

Правило 4: call и apply

Они позволяют явно задать this.

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

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

// call: передаешь this и аргументы
greet.call(user, 'Hello'); // 'Hello, Иван' (this = user)

// apply: то же, но аргументы в массиве
greet.apply(user, ['Hi']); // 'Hi, Иван' (this = user)

// bind: возвращает новую функцию с зафиксированным this
const boundGreet = greet.bind(user, 'Hey');
boundGreet(); // 'Hey, Иван' (this всегда = user)

Правило 5: Constructor (new)

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

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

const user = new User('Иван');
user.greet(); // 'Hello, Иван' (this = user)

Классы и this

class User {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    console.log(`Hello, ${this.name}`);
  }
}

const user = new User('Иван');
user.greet(); // 'Hello, Иван' (this = user)

// НО: если присвоишь метод переменной
const greet = user.greet;
greet(); // ОШИБКА: Cannot read property 'name' of undefined

// Решение: bind
const boundGreet = user.greet.bind(user);
boundGreet(); // 'Hello, Иван'

Частая проблема: обработчики событий

class Button {
  constructor(name) {
    this.name = name;
    this.el = document.querySelector('button');
  }
  
  // ПЛОХО
  attachBad() {
    this.el.addEventListener('click', function() {
      console.log(this.name); // undefined (this = элемент)
    });
  }
  
  // ХОРОШО: стрелочная функция
  attachGood() {
    this.el.addEventListener('click', () => {
      console.log(this.name); // 'Button name' (this = Button)
    });
  }
  
  // ХОРОШО: bind
  attachGood2() {
    this.el.addEventListener('click', function() {
      console.log(this.name); // 'Button name'
    }.bind(this));
  }
}

Обзор: что будет this

Таблица this значений

// 1. Метод объекта
obj.method(); // this = obj

// 2. Обычная функция
function f() {} 
f(); // this = undefined (strict) или window

// 3. Стрелочная функция
const arrow = () => this; // this из области видимости

// 4. call/apply/bind
f.call(context); // this = context
f.apply(context, args); // this = context
f.bind(context); // this = context (новая функция)

// 5. new
new Constructor(); // this = новый объект

// 6. Обработчик события
element.addEventListener('click', function() {
  // this = element (если не стрелочная)
});

element.addEventListener('click', () => {
  // this из окружения
});

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

Пример 1: Класс с методами

class Counter {
  constructor(initial = 0) {
    this.count = initial;
    // bind нужен для передачи как callback
    this.increment = this.increment.bind(this);
  }
  
  increment() {
    this.count++;
    console.log(this.count);
  }
}

const counter = new Counter();
const btn = document.querySelector('button');
btn.addEventListener('click', counter.increment); // Работает!

Пример 2: React компонент

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    // bind в конструкторе
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }
  
  render() {
    return <button onClick={this.handleClick}>+</button>;
  }
}

// Или используй стрелочную функцию (property initializer)
class MyComponent2 extends React.Component {
  state = { count: 0 };
  
  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  };
  
  render() {
    return <button onClick={this.handleClick}>+</button>;
  }
}

**Пример 3: setTimeout

const user = {
  name: 'Иван',
  sayHello() {
    console.log(`Hello, ${this.name}`);
  }
};

setTimeout(user.sayHello, 1000);
// НЕВЕРНО: 'Hello, undefined' (this потеряется)

setTimeout(user.sayHello.bind(user), 1000);
// ВЕРНО: 'Hello, Иван'

setTimeout(() => user.sayHello(), 1000);
// ВЕРНО: 'Hello, Иван' (стрелочная функция)

Отладка this

function debugThis() {
  console.log('this =', this);
  console.log('this.constructor =', this.constructor?.name);
}

const obj = {};
obj.method = debugThis;
obj.method(); // this = { method: debugThis }

debugThis(); // this = undefined (strict) или window

Заключение

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

  • Метод объекта: this = объект
  • Обычная функция: this = undefined/window
  • Стрелочная функция: this из области видимости
  • call/apply/bind: явно задаешь this
  • new: this = новый объект

Это может быть сложным, но с практикой станет ясно.

Вызывается ли watchEffect только при изменении | PrepBro