this в JavaScript определяется не местом определения функции, а **способом её вызова**. Четыре основных правила: по умолчанию this — глобальный объект (или undefined в strict mode); при вызове метода объекта — объект перед точкой; при явном задании через call/apply/bind; при вызове через new — новый объект. Стрелочные функции не имеют собственного this — они захватывают его из лексического окружения при создании.
function show() {
console.log(this)
}
show() // window (браузер) или global (Node.js)
// undefined в strict mode ('use strict')В строгом режиме this по умолчанию — undefined, а не глобальный объект. Это сделано специально чтобы предотвращать случайное загрязнение глобальных переменных.
this — объект **перед последней точкой** при вызове:
const user = {
name: 'Alice',
greet() {
console.log(`Привет, я ${this.name}`)
}
}
user.greet() // 'Привет, я Alice' — this = user
// Вложенный объект: this = объект перед точкой
const app = {
user: user,
run() { this.user.greet() } // this.user = {name: 'Alice', greet: fn}
}
// Потеря контекста — классический баг!
const fn = user.greet // функция без привязки к объекту
fn() // 'Привет, я undefined' — this = global/undefinedfunction greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`)
}
const alice = { name: 'Alice' }
const bob = { name: 'Bob' }
// call — аргументы через запятую, вызывает сразу
greet.call(alice, 'Привет', '!') // 'Привет, Alice!'
greet.call(bob, 'Здравствуй', '.') // 'Здравствуй, Bob.'
// apply — аргументы в массиве, вызывает сразу
greet.apply(alice, ['Хай', '?']) // 'Хай, Alice?'
// bind — возвращает новую функцию с привязанным this (НЕ вызывает)
const greetAlice = greet.bind(alice, 'Добрый день')
greetAlice('!') // 'Добрый день, Alice!'
greetAlice('.') // 'Добрый день, Alice.'При вызове функции через new:
1. Создаётся новый пустой объект
2. this указывает на этот объект
3. Выполняется тело функции
4. Новый объект возвращается автоматически
function Person(name, age) {
this.name = name // this = новый объект
this.age = age
// return this — неявно
}
const alice = new Person('Alice', 30)
console.log(alice.name) // 'Alice'
console.log(alice.age) // 30new binding > (высший приоритет)
explicit binding >
implicit binding >
default binding (низший приоритет)Стрелочная функция **не имеет** собственного this. Она захватывает this из окружающего лексического контекста при создании — и этот this никогда не изменяется.
const timer = {
seconds: 0,
// ПРОБЛЕМА: обычная функция теряет this
startBroken() {
setInterval(function() {
this.seconds++ // this = global/undefined (не timer!)
console.log(this.seconds) // NaN или ошибка
}, 1000)
},
// РЕШЕНИЕ 1: стрелочная функция захватывает this из startCorrect
startArrow() {
setInterval(() => {
this.seconds++ // this = timer (захвачен из startArrow)
console.log(this.seconds) // 1, 2, 3...
}, 1000)
},
// РЕШЕНИЕ 2: bind
startBind() {
setInterval(function() {
this.seconds++
console.log(this.seconds)
}.bind(this), 1000)
}
}// НЕ используй стрелочную функцию как метод объекта
const obj = {
name: 'Тест',
greet: () => {
// this здесь = лексическое this снаружи (обычно global/undefined)
console.log(this.name) // undefined!
}
}
obj.greet() // undefined — потеря контекста!
// Используй обычную функцию для методов
const obj2 = {
name: 'Тест',
greet() {
console.log(this.name) // 'Тест' — правильно!
}
}
obj2.greet() // 'Тест'function greet() { return this.name }
const alice = { name: 'Alice' }
const bound = greet.bind(alice)
const bob = { name: 'Bob' }
bound.call(bob) // 'Alice' — bind сильнее call!
bound.apply(bob) // 'Alice' — bind сильнее apply!
// Единственное исключение — new (для полифиллов)**Сразу скажи главное**: "this определяется не где написана функция, а как она вызвана."
**Объясни 4 правила по порядку**: default → implicit (метод) → explicit (call/apply/bind) → new. Для каждого — 1-2 предложения и короткий пример.
**Стрелочные функции** — отдельный пункт: "Стрелочные функции не имеют собственного this. Они захватывают this из лексического окружения при создании."
**Покажи классический баг**: потеря this в setTimeout/setInterval и два способа исправить (bind и стрелка).
**Время ответа**: 3-4 минуты.
1. **"this — это сама функция"** или **"this — это объект, в котором определена функция"** — оба варианта неверны. this зависит от вызова, не от определения.
2. **Не знать про потерю контекста** — это самый частый баг с this в реальном коде. "Передал метод в setTimeout и сломалось" — классика. Незнание этого выдаёт джуниора.
3. **Не понимать разницу call/apply/bind** — call и apply вызывают сразу, bind возвращает функцию. Путаница — признак поверхностного знания.
Все четыре правила this + стрелочные функции + решения для потери контекста
'use strict'
// В strict mode this по умолчанию = undefined (безопаснее)
// ===== ПРАВИЛО 1: Default binding =====
function defaultThis() {
return typeof this // 'undefined' в strict mode
}
console.log('Default this:', defaultThis()) // 'undefined'
// ===== ПРАВИЛО 2: Implicit binding =====
const user = {
name: 'Alice',
role: 'admin',
greet() {
return `Привет, ${this.name} (${this.role})`
},
getInfo() {
return { name: this.name, role: this.role }
}
}
console.log('\nImplicit binding:')
console.log(user.greet()) // 'Привет, Alice (admin)'
// Потеря контекста
const greetFn = user.greet
try {
console.log(greetFn()) // TypeError в strict mode
} catch(e) {
console.log('Потеря контекста:', e.constructor.name) // TypeError
}
// ===== ПРАВИЛО 3: Explicit binding =====
console.log('\nExplicit binding:')
function introduce(greeting, lang) {
return `[${lang}] ${greeting}, я ${this.name}!`
}
const alice = { name: 'Alice' }
const bob = { name: 'Bob' }
// call — аргументы через запятую
console.log(introduce.call(alice, 'Привет', 'RU')) // [RU] Привет, я Alice!
console.log(introduce.call(bob, 'Hello', 'EN')) // [EN] Hello, я Bob!
// apply — аргументы в массиве
console.log(introduce.apply(alice, ['Хай', 'RU'])) // [RU] Хай, я Alice!
// bind — возвращает новую функцию
const aliceGreeter = introduce.bind(alice, 'Добрый день')
console.log(aliceGreeter('RU')) // [RU] Добрый день, я Alice!
console.log(aliceGreeter('EN')) // [EN] Добрый день, я Alice!
// bind нельзя перебить
console.log(aliceGreeter.call(bob, 'RU')) // [RU] Добрый день, я Alice! (не Bob!)
// ===== ПРАВИЛО 4: new binding =====
console.log('\nnew binding:')
function Animal(name, sound) {
this.name = name
this.sound = sound
this.speak = function() {
return `${this.name} говорит: ${this.sound}!`
}
}
const cat = new Animal('Кот', 'Мяу')
const dog = new Animal('Пёс', 'Гав')
console.log(cat.speak()) // Кот говорит: Мяу!
console.log(dog.speak()) // Пёс говорит: Гав!
// ===== СТРЕЛОЧНЫЕ ФУНКЦИИ =====
console.log('\nСтрелочные функции:')
const timer = {
name: 'Timer',
ticks: 0,
// ПРОБЛЕМА: обычная функция в колбэке теряет this
// startBroken() { setTimeout(function() { this.ticks++ }, 0) }
// РЕШЕНИЕ 1: стрелочная функция
startArrow() {
// this здесь = timer (из лексического контекста startArrow)
const tick = () => {
this.ticks++
return `${this.name}: tick #${this.ticks}`
}
return tick()
},
// РЕШЕНИЕ 2: bind
startBind() {
const tick = function() {
this.ticks++
return `${this.name}: tick #${this.ticks}`
}.bind(this)
return tick()
}
}
console.log(timer.startArrow()) // Timer: tick #1
console.log(timer.startBind()) // Timer: tick #2
// Стрелочная функция как метод — ПЛОХО
const broken = {
name: 'broken',
greet: () => {
// this здесь = внешнее this (undefined в strict mode или global)
return `this.name = ${typeof this === 'undefined' ? 'undefined' : String(this)}`
}
}
console.log('\nСтрелка как метод (плохо):', broken.greet())this в JavaScript определяется не местом определения функции, а **способом её вызова**. Четыре основных правила: по умолчанию this — глобальный объект (или undefined в strict mode); при вызове метода объекта — объект перед точкой; при явном задании через call/apply/bind; при вызове через new — новый объект. Стрелочные функции не имеют собственного this — они захватывают его из лексического окружения при создании.
function show() {
console.log(this)
}
show() // window (браузер) или global (Node.js)
// undefined в strict mode ('use strict')В строгом режиме this по умолчанию — undefined, а не глобальный объект. Это сделано специально чтобы предотвращать случайное загрязнение глобальных переменных.
this — объект **перед последней точкой** при вызове:
const user = {
name: 'Alice',
greet() {
console.log(`Привет, я ${this.name}`)
}
}
user.greet() // 'Привет, я Alice' — this = user
// Вложенный объект: this = объект перед точкой
const app = {
user: user,
run() { this.user.greet() } // this.user = {name: 'Alice', greet: fn}
}
// Потеря контекста — классический баг!
const fn = user.greet // функция без привязки к объекту
fn() // 'Привет, я undefined' — this = global/undefinedfunction greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`)
}
const alice = { name: 'Alice' }
const bob = { name: 'Bob' }
// call — аргументы через запятую, вызывает сразу
greet.call(alice, 'Привет', '!') // 'Привет, Alice!'
greet.call(bob, 'Здравствуй', '.') // 'Здравствуй, Bob.'
// apply — аргументы в массиве, вызывает сразу
greet.apply(alice, ['Хай', '?']) // 'Хай, Alice?'
// bind — возвращает новую функцию с привязанным this (НЕ вызывает)
const greetAlice = greet.bind(alice, 'Добрый день')
greetAlice('!') // 'Добрый день, Alice!'
greetAlice('.') // 'Добрый день, Alice.'При вызове функции через new:
1. Создаётся новый пустой объект
2. this указывает на этот объект
3. Выполняется тело функции
4. Новый объект возвращается автоматически
function Person(name, age) {
this.name = name // this = новый объект
this.age = age
// return this — неявно
}
const alice = new Person('Alice', 30)
console.log(alice.name) // 'Alice'
console.log(alice.age) // 30new binding > (высший приоритет)
explicit binding >
implicit binding >
default binding (низший приоритет)Стрелочная функция **не имеет** собственного this. Она захватывает this из окружающего лексического контекста при создании — и этот this никогда не изменяется.
const timer = {
seconds: 0,
// ПРОБЛЕМА: обычная функция теряет this
startBroken() {
setInterval(function() {
this.seconds++ // this = global/undefined (не timer!)
console.log(this.seconds) // NaN или ошибка
}, 1000)
},
// РЕШЕНИЕ 1: стрелочная функция захватывает this из startCorrect
startArrow() {
setInterval(() => {
this.seconds++ // this = timer (захвачен из startArrow)
console.log(this.seconds) // 1, 2, 3...
}, 1000)
},
// РЕШЕНИЕ 2: bind
startBind() {
setInterval(function() {
this.seconds++
console.log(this.seconds)
}.bind(this), 1000)
}
}// НЕ используй стрелочную функцию как метод объекта
const obj = {
name: 'Тест',
greet: () => {
// this здесь = лексическое this снаружи (обычно global/undefined)
console.log(this.name) // undefined!
}
}
obj.greet() // undefined — потеря контекста!
// Используй обычную функцию для методов
const obj2 = {
name: 'Тест',
greet() {
console.log(this.name) // 'Тест' — правильно!
}
}
obj2.greet() // 'Тест'function greet() { return this.name }
const alice = { name: 'Alice' }
const bound = greet.bind(alice)
const bob = { name: 'Bob' }
bound.call(bob) // 'Alice' — bind сильнее call!
bound.apply(bob) // 'Alice' — bind сильнее apply!
// Единственное исключение — new (для полифиллов)**Сразу скажи главное**: "this определяется не где написана функция, а как она вызвана."
**Объясни 4 правила по порядку**: default → implicit (метод) → explicit (call/apply/bind) → new. Для каждого — 1-2 предложения и короткий пример.
**Стрелочные функции** — отдельный пункт: "Стрелочные функции не имеют собственного this. Они захватывают this из лексического окружения при создании."
**Покажи классический баг**: потеря this в setTimeout/setInterval и два способа исправить (bind и стрелка).
**Время ответа**: 3-4 минуты.
1. **"this — это сама функция"** или **"this — это объект, в котором определена функция"** — оба варианта неверны. this зависит от вызова, не от определения.
2. **Не знать про потерю контекста** — это самый частый баг с this в реальном коде. "Передал метод в setTimeout и сломалось" — классика. Незнание этого выдаёт джуниора.
3. **Не понимать разницу call/apply/bind** — call и apply вызывают сразу, bind возвращает функцию. Путаница — признак поверхностного знания.
Все четыре правила this + стрелочные функции + решения для потери контекста
'use strict'
// В strict mode this по умолчанию = undefined (безопаснее)
// ===== ПРАВИЛО 1: Default binding =====
function defaultThis() {
return typeof this // 'undefined' в strict mode
}
console.log('Default this:', defaultThis()) // 'undefined'
// ===== ПРАВИЛО 2: Implicit binding =====
const user = {
name: 'Alice',
role: 'admin',
greet() {
return `Привет, ${this.name} (${this.role})`
},
getInfo() {
return { name: this.name, role: this.role }
}
}
console.log('\nImplicit binding:')
console.log(user.greet()) // 'Привет, Alice (admin)'
// Потеря контекста
const greetFn = user.greet
try {
console.log(greetFn()) // TypeError в strict mode
} catch(e) {
console.log('Потеря контекста:', e.constructor.name) // TypeError
}
// ===== ПРАВИЛО 3: Explicit binding =====
console.log('\nExplicit binding:')
function introduce(greeting, lang) {
return `[${lang}] ${greeting}, я ${this.name}!`
}
const alice = { name: 'Alice' }
const bob = { name: 'Bob' }
// call — аргументы через запятую
console.log(introduce.call(alice, 'Привет', 'RU')) // [RU] Привет, я Alice!
console.log(introduce.call(bob, 'Hello', 'EN')) // [EN] Hello, я Bob!
// apply — аргументы в массиве
console.log(introduce.apply(alice, ['Хай', 'RU'])) // [RU] Хай, я Alice!
// bind — возвращает новую функцию
const aliceGreeter = introduce.bind(alice, 'Добрый день')
console.log(aliceGreeter('RU')) // [RU] Добрый день, я Alice!
console.log(aliceGreeter('EN')) // [EN] Добрый день, я Alice!
// bind нельзя перебить
console.log(aliceGreeter.call(bob, 'RU')) // [RU] Добрый день, я Alice! (не Bob!)
// ===== ПРАВИЛО 4: new binding =====
console.log('\nnew binding:')
function Animal(name, sound) {
this.name = name
this.sound = sound
this.speak = function() {
return `${this.name} говорит: ${this.sound}!`
}
}
const cat = new Animal('Кот', 'Мяу')
const dog = new Animal('Пёс', 'Гав')
console.log(cat.speak()) // Кот говорит: Мяу!
console.log(dog.speak()) // Пёс говорит: Гав!
// ===== СТРЕЛОЧНЫЕ ФУНКЦИИ =====
console.log('\nСтрелочные функции:')
const timer = {
name: 'Timer',
ticks: 0,
// ПРОБЛЕМА: обычная функция в колбэке теряет this
// startBroken() { setTimeout(function() { this.ticks++ }, 0) }
// РЕШЕНИЕ 1: стрелочная функция
startArrow() {
// this здесь = timer (из лексического контекста startArrow)
const tick = () => {
this.ticks++
return `${this.name}: tick #${this.ticks}`
}
return tick()
},
// РЕШЕНИЕ 2: bind
startBind() {
const tick = function() {
this.ticks++
return `${this.name}: tick #${this.ticks}`
}.bind(this)
return tick()
}
}
console.log(timer.startArrow()) // Timer: tick #1
console.log(timer.startBind()) // Timer: tick #2
// Стрелочная функция как метод — ПЛОХО
const broken = {
name: 'broken',
greet: () => {
// this здесь = внешнее this (undefined в strict mode или global)
return `this.name = ${typeof this === 'undefined' ? 'undefined' : String(this)}`
}
}
console.log('\nСтрелка как метод (плохо):', broken.greet())Найди и исправь все проблемы с this в коде. В коде 4 места где this работает неправильно — используй bind, стрелочные функции или рефакторинг чтобы исправить.
Проблема 1: const inc = counter.increment.bind(counter). Проблема 2: замени function() на стрелочную () =>. Проблема 3: замени greet: () => {} на greet() {}. Проблема 4: замени function(num) на (num) => или добавь .bind(this) к forEach колбэку. Бонус: return fn.bind(context).
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке