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

Как контекст функции зависит от Scope?

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

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

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

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

Контекст (this) и область видимости (Scope)

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

Важное уточнение: это РАЗНЫЕ концепции

Люди часто путают this (контекст) и scope (область видимости). Это не одно и то же:

  • Scope определяет ДОСТУПНОСТЬ переменных
  • This определяет ТЕКУЩИЙ ОБЪЕКТ (контекст)
const person = {
  name: 'John',
  
  greet() {
    // Scope: имеем доступ к переменным в этой функции, родительском объекте, глобальной области
    console.log(this.name); // This указывает на объект person
  }
};

person.greet(); // 'John'

Правило определения This: 4 способа привязки

1. Привязка по контексту вызова (Default Binding)

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

const user = {
  name: 'Alice',
  getName: function() {
    return this.name; // This = user
  }
};

console.log(user.getName()); // 'Alice'

// Но если присвоить функцию переменной:
const fn = user.getName;
console.log(fn()); // undefined (this = window/global в non-strict mode)

2. Явная привязка (Explicit Binding) - call, apply, bind

Можешь явно указать значение this:

function introduce() {
  console.log(`Привет, я ${this.name}`);
}

const person1 = { name: 'John' };
const person2 = { name: 'Jane' };

// call - вызывает функцию сразу с указанным this
introduce.call(person1); // 'Привет, я John'
introduce.call(person2); // 'Привет, я Jane'

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

greet.apply(person1, ['Привет', '!']); // 'Привет, я John!'

// bind - возвращает новую функцию с привязанным this
const boundGreet = greet.bind(person1, 'Хай');
boundGreet('...'); // 'Хай, я John...'

3. Конструкторная привязка (Constructor Binding)

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

function Person(name) {
  this.name = name; // This указывает на новый создаваемый объект
}

const john = new Person('John');
console.log(john.name); // 'John'

4. Стрелочные функции (Lexical This)

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

const obj = {
  name: 'Object',
  
  regularMethod() {
    console.log(this.name); // 'Object' - this зависит от вызова
  },
  
  arrowMethod: () => {
    console.log(this.name); // undefined - this из глобального scope
  },
  
  complexCase() {
    // Обычная функция
    const regularFn = function() {
      console.log(this); // this = undefined (в strict mode) или window
    };
    
    // Стрелочная функция
    const arrowFn = () => {
      console.log(this); // this = obj (из окружающего scope)
    };
    
    regularFn();
    arrowFn();
  }
};

obj.complexCase();
// Обычная функция: undefined
// Стрелочная: объект Object

Взаимосвязь Scope и This

Scope определяет доступ к переменным

let global = 'глобальная';

function outer() {
  let outerVar = 'из outer';
  
  function inner() {
    let innerVar = 'из inner';
    
    // Scope позволяет обращаться ко ВСЕМ этим переменным
    console.log(global);    // 'глобальная' - из глобального scope
    console.log(outerVar);  // 'из outer' - из родительского scope
    console.log(innerVar);  // 'из inner' - локальный scope
  }
  
  inner();
}

outer();

This НЕ зависит от Scope

const obj = {
  name: 'Object',
  method() {
    // Scope: имеем доступ к переменным функции и объекта
    // This: указывает на obj
    
    function nested() {
      // Scope: ТОЖЕ имеем доступ (через замыкание)
      // This: НЕ имеем доступа к obj.this! This = undefined
      console.log(this); // undefined (не obj!)
    }
    
    nested();
  }
};

obj.method();

Практический пример: обработчик событий

const button = {
  label: 'Click me',
  clickHandler: function() {
    console.log(this.label); // ПРОБЛЕМА: this = element, не button!
  }
};

const domButton = document.querySelector('button');
domButton.addEventListener('click', button.clickHandler);
// При клике: this = domButton элемент, не button объект!

// Решение 1: стрелочная функция
document.addEventListener('click', () => {
  console.log(this); // this из окружающего scope
});

// Решение 2: bind
document.addEventListener('click', button.clickHandler.bind(button));

// Решение 3: обёртка
document.addEventListener('click', () => button.clickHandler());

Правило приоритета привязки This

Последовательность приоритета (от выше к ниже):

1. new (конструктор) - создаёт новый объект
2. call/apply/bind (явная) - явно указано
3. obj.method() (контекст) - метод объекта
4. function() (по умолчанию) - функция как таковая

Сложный пример: комбинированный случай

const outer = {
  x: 'outer',
  
  method: function() {
    console.log(this.x); // 'outer' - this из контекста вызова
    
    const inner = function() {
      console.log(this.x); // undefined - функция не имеет контекста
    };
    
    const arrowInner = () => {
      console.log(this.x); // 'outer' - наследует this из method
    };
    
    const boundInner = function() {
      console.log(this.x); // 'custom' - привязано явно
    }.bind({ x: 'custom' });
    
    inner();        // undefined
    arrowInner();   // 'outer'
    boundInner();   // 'custom'
  }
};

outer.method();

Чеклист для определения This

Когда видишь функцию, спроси себя:

  1. Вызывается ли через new? -> this = новый объект
  2. Вызывается ли через .call(), .apply(), .bind()? -> this = явно указанное значение
  3. Вызывается ли как метод объекта obj.method()? -> this = объект
  4. Стрелочная функция? -> this наследует из родительского scope
  5. Обычная функция? -> this = undefined (strict) или window/global (non-strict)

Вывод: this (контекст) и scope (область видимости) это разные системы. Scope определяет доступность переменных, this определяет текущий объект. Стрелочные функции наследуют this из scope, обычные функции определяют this по контексту вызова.