В JavaScript каждый объект имеет внутреннюю ссылку [[Prototype]] на другой объект (или null). При обращении к свойству движок сначала ищет его в самом объекте, затем поднимается по цепочке прототипов вверх до null. Это и есть прототипное наследование. Классы в JS — синтаксический сахар над этим же механизмом: class под капотом создаёт функцию-конструктор и настраивает прототипную цепочку.
const animal = {
speak() { return `${this.name} говорит` }
}
const dog = Object.create(animal) // dog.[[Prototype]] = animal
dog.name = 'Рекс'
console.log(dog.name) // 'Рекс' — собственное свойство dog
console.log(dog.speak()) // 'Рекс говорит' — взято из прототипа!dog.speak() ← ищем в dog... нет
← ищем в animal... НАШЛИ → выполняем
← если бы не нашли → ищем в Object.prototype
← если бы не нашли → null → undefineddog
│ name: 'Рекс'
│ [[Prototype]] ──→ animal
│ speak: function
│ [[Prototype]] ──→ Object.prototype
│ hasOwnProperty: function
│ toString: function
│ [[Prototype]] ──→ null// Получить прототип объекта
Object.getPrototypeOf(dog) === animal // true
// Проверить принадлежность свойства
dog.hasOwnProperty('name') // true — собственное
dog.hasOwnProperty('speak') // false — унаследованное
// Проверить вхождение в цепочку
dog instanceof Object // true — Object.prototype есть в цепочке
// __proto__ — устаревший способ, избегай в продакшне
dog.__proto__ === animal // true (работает, но не рекомендуется)До ES6 наследование строилось через функции-конструкторы:
// Конструктор
function Animal(name) {
this.name = name // собственное свойство
}
// Метод на прототипе — общий для всех экземпляров
Animal.prototype.speak = function() {
return `${this.name} говорит`
}
// Наследование
function Dog(name, breed) {
Animal.call(this, name) // вызов "super"
this.breed = breed
}
// Настройка цепочки прототипов
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog // важно восстановить!
Dog.prototype.bark = function() {
return 'Гав!'
}
const rex = new Dog('Рекс', 'Лабрадор')
console.log(rex.speak()) // 'Рекс говорит' (из Animal.prototype)
console.log(rex.bark()) // 'Гав!' (из Dog.prototype)
console.log(rex instanceof Dog) // true
console.log(rex instanceof Animal) // true// ES6 класс...
class Animal {
constructor(name) {
this.name = name
}
speak() {
return `${this.name} говорит`
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name) // Animal.call(this, name)
this.breed = breed
}
bark() { return 'Гав!' }
}
// ...компилируется в ТО ЖЕ САМОЕ, что выше!
// typeof Animal === 'function' — это по-прежнему функция!
const rex = new Dog('Рекс', 'Лабрадор')
console.log(Object.getPrototypeOf(rex) === Dog.prototype) // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype) // trueОчень частая путаница:
Function.prototype — свойство функции-конструктора
(объект, который станет [[Prototype]] экземпляров)
[[Prototype]] — внутренняя ссылка каждого объекта
(доступна через Object.getPrototypeOf())
Dog.prototype ← объект с методами Dog
rex.[[Prototype]] ← то же самое что Dog.prototype
Dog.[[Prototype]] ← Function.prototype (Dog сам — функция)const obj = Object.create({ inherited: true })
obj.own = true
'own' in obj // true — ищет по всей цепочке
'inherited' in obj // true — нашёл в прототипе
obj.hasOwnProperty('own') // true — только собственные
obj.hasOwnProperty('inherited') // false — не собственноеconst dict = Object.create(null) // [[Prototype]] = null
dict.key = 'value'
// Нет методов Object.prototype — чистый словарь
dict.hasOwnProperty // undefined (нет прототипа!)
dict.toString // undefined
// Используют как "чистые" хеш-таблицы без риска конфликта с прототипом**Начни с механизма**: "В JS нет классического наследования как в Java. Вместо этого объекты связаны цепочкой прототипов. При обращении к свойству JS ищет его сначала в объекте, затем поднимается по цепочке."
**Объясни связь с классами**: "Классы — синтаксический сахар. Под капотом — те же функции-конструкторы и прототипы."
**Покажи Object.create**: это самый прямой способ показать прототипное наследование без магии new/class.
**Объясни путаницу prototype vs [[Prototype]]** — это отличает джуниора от мидла.
1. **"В JS есть классы как в Java"** — нет. Классы в JS — синтаксический сахар. typeof MyClass === 'function'. Это принципиально другая модель.
2. **Путаница Dog.prototype и dog.[[Prototype]]** — не понимать разницу между свойством конструктора и внутренней ссылкой объекта означает непонимание базового механизма.
3. **"hasOwnProperty и in делают одно и то же"** — нет. in ищет по всей цепочке, hasOwnProperty — только в самом объекте.
Прототипная цепочка вручную, затем то же самое через классы — показываем что это одно и то же
// ===== 1. ПРОТОТИПНАЯ ЦЕПОЧКА ВРУЧНУЮ =====
console.log('=== Ручные прототипы ===')
// Базовый объект
const Vehicle = {
type: 'транспортное средство',
describe() {
return `${this.brand || '?'} — ${this.type}`
},
toString() {
return `[${this.type}: ${this.brand || '?'}]`
}
}
// Создаём объект с Vehicle как прототипом
const Car = Object.create(Vehicle)
Car.type = 'автомобиль'
Car.drive = function() {
return `${this.brand} едет`
}
// Конкретный экземпляр
const tesla = Object.create(Car)
tesla.brand = 'Tesla'
tesla.model = 'Model S'
// Поиск по цепочке:
console.log(tesla.brand) // 'Tesla' — собственное
console.log(tesla.type) // 'автомобиль' — из Car
console.log(tesla.describe()) // 'Tesla — автомобиль' — из Vehicle
console.log(tesla.drive()) // 'Tesla едет' — из Car
// Цепочка прототипов
console.log('\n--- Цепочка прототипов ---')
let proto = tesla
let depth = 0
while (proto !== null) {
const props = Object.getOwnPropertyNames(proto)
.filter(k => k !== '__proto__')
console.log(`[${depth}] ${proto === tesla ? 'tesla' : proto === Car ? 'Car' : proto === Vehicle ? 'Vehicle' : 'Object.prototype'}: [${props.join(', ')}]`)
proto = Object.getPrototypeOf(proto)
depth++
}
// hasOwnProperty vs in
console.log('\n--- hasOwnProperty vs in ---')
console.log('brand - own?', tesla.hasOwnProperty('brand')) // true
console.log('type - own?', tesla.hasOwnProperty('type')) // false
console.log('describe - in?', 'describe' in tesla) // true
console.log('toString - in?', 'toString' in tesla) // true
// ===== 2. КЛАССЫ = ТЕ ЖЕ ПРОТОТИПЫ =====
console.log('\n=== Классы под капотом ===')
class Animal {
constructor(name, sound) {
this.name = name // собственное свойство
this.sound = sound // собственное свойство
}
speak() { // метод — на Animal.prototype
return `${this.name}: ${this.sound}!`
}
toString() {
return `Animal(name=${this.name})`
}
}
class Dog extends Animal {
constructor(name) {
super(name, 'Гав') // вызов Animal.constructor
this.tricks = [] // собственное свойство
}
learn(trick) { // метод — на Dog.prototype
this.tricks.push(trick)
return this
}
perform() {
return this.tricks.length > 0
? `${this.name} умеет: ${this.tricks.join(', ')}`
: `${this.name} ничему не обучен`
}
}
const rex = new Dog('Рекс')
rex.learn('сидеть').learn('лежать').learn('кувырок')
console.log(rex.speak()) // Рекс: Гав!
console.log(rex.perform()) // Рекс умеет: сидеть, лежать, кувырок
// Доказательство: это те же прототипы
console.log('\n--- Доказательство: class = прототипы ---')
console.log('typeof Animal:', typeof Animal) // function
console.log('rex.[[Prototype]] === Dog.prototype:',
Object.getPrototypeOf(rex) === Dog.prototype) // true
console.log('Dog.prototype.[[Prototype]] === Animal.prototype:',
Object.getPrototypeOf(Dog.prototype) === Animal.prototype) // true
console.log('Animal.prototype.[[Prototype]] === Object.prototype:',
Object.getPrototypeOf(Animal.prototype) === Object.prototype) // true
// Собственные свойства vs унаследованные
console.log('\n--- Собственные свойства rex ---')
console.log(Object.getOwnPropertyNames(rex)) // ['name', 'sound', 'tricks']
console.log('--- Методы на Dog.prototype ---')
console.log(Object.getOwnPropertyNames(Dog.prototype)) // ['constructor', 'learn', 'perform']
// instanceof проверяет цепочку прототипов
console.log('\n--- instanceof ---')
console.log('rex instanceof Dog:', rex instanceof Dog) // true
console.log('rex instanceof Animal:', rex instanceof Animal) // true
console.log('rex instanceof Object:', rex instanceof Object) // trueВ JavaScript каждый объект имеет внутреннюю ссылку [[Prototype]] на другой объект (или null). При обращении к свойству движок сначала ищет его в самом объекте, затем поднимается по цепочке прототипов вверх до null. Это и есть прототипное наследование. Классы в JS — синтаксический сахар над этим же механизмом: class под капотом создаёт функцию-конструктор и настраивает прототипную цепочку.
const animal = {
speak() { return `${this.name} говорит` }
}
const dog = Object.create(animal) // dog.[[Prototype]] = animal
dog.name = 'Рекс'
console.log(dog.name) // 'Рекс' — собственное свойство dog
console.log(dog.speak()) // 'Рекс говорит' — взято из прототипа!dog.speak() ← ищем в dog... нет
← ищем в animal... НАШЛИ → выполняем
← если бы не нашли → ищем в Object.prototype
← если бы не нашли → null → undefineddog
│ name: 'Рекс'
│ [[Prototype]] ──→ animal
│ speak: function
│ [[Prototype]] ──→ Object.prototype
│ hasOwnProperty: function
│ toString: function
│ [[Prototype]] ──→ null// Получить прототип объекта
Object.getPrototypeOf(dog) === animal // true
// Проверить принадлежность свойства
dog.hasOwnProperty('name') // true — собственное
dog.hasOwnProperty('speak') // false — унаследованное
// Проверить вхождение в цепочку
dog instanceof Object // true — Object.prototype есть в цепочке
// __proto__ — устаревший способ, избегай в продакшне
dog.__proto__ === animal // true (работает, но не рекомендуется)До ES6 наследование строилось через функции-конструкторы:
// Конструктор
function Animal(name) {
this.name = name // собственное свойство
}
// Метод на прототипе — общий для всех экземпляров
Animal.prototype.speak = function() {
return `${this.name} говорит`
}
// Наследование
function Dog(name, breed) {
Animal.call(this, name) // вызов "super"
this.breed = breed
}
// Настройка цепочки прототипов
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog // важно восстановить!
Dog.prototype.bark = function() {
return 'Гав!'
}
const rex = new Dog('Рекс', 'Лабрадор')
console.log(rex.speak()) // 'Рекс говорит' (из Animal.prototype)
console.log(rex.bark()) // 'Гав!' (из Dog.prototype)
console.log(rex instanceof Dog) // true
console.log(rex instanceof Animal) // true// ES6 класс...
class Animal {
constructor(name) {
this.name = name
}
speak() {
return `${this.name} говорит`
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name) // Animal.call(this, name)
this.breed = breed
}
bark() { return 'Гав!' }
}
// ...компилируется в ТО ЖЕ САМОЕ, что выше!
// typeof Animal === 'function' — это по-прежнему функция!
const rex = new Dog('Рекс', 'Лабрадор')
console.log(Object.getPrototypeOf(rex) === Dog.prototype) // true
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype) // trueОчень частая путаница:
Function.prototype — свойство функции-конструктора
(объект, который станет [[Prototype]] экземпляров)
[[Prototype]] — внутренняя ссылка каждого объекта
(доступна через Object.getPrototypeOf())
Dog.prototype ← объект с методами Dog
rex.[[Prototype]] ← то же самое что Dog.prototype
Dog.[[Prototype]] ← Function.prototype (Dog сам — функция)const obj = Object.create({ inherited: true })
obj.own = true
'own' in obj // true — ищет по всей цепочке
'inherited' in obj // true — нашёл в прототипе
obj.hasOwnProperty('own') // true — только собственные
obj.hasOwnProperty('inherited') // false — не собственноеconst dict = Object.create(null) // [[Prototype]] = null
dict.key = 'value'
// Нет методов Object.prototype — чистый словарь
dict.hasOwnProperty // undefined (нет прототипа!)
dict.toString // undefined
// Используют как "чистые" хеш-таблицы без риска конфликта с прототипом**Начни с механизма**: "В JS нет классического наследования как в Java. Вместо этого объекты связаны цепочкой прототипов. При обращении к свойству JS ищет его сначала в объекте, затем поднимается по цепочке."
**Объясни связь с классами**: "Классы — синтаксический сахар. Под капотом — те же функции-конструкторы и прототипы."
**Покажи Object.create**: это самый прямой способ показать прототипное наследование без магии new/class.
**Объясни путаницу prototype vs [[Prototype]]** — это отличает джуниора от мидла.
1. **"В JS есть классы как в Java"** — нет. Классы в JS — синтаксический сахар. typeof MyClass === 'function'. Это принципиально другая модель.
2. **Путаница Dog.prototype и dog.[[Prototype]]** — не понимать разницу между свойством конструктора и внутренней ссылкой объекта означает непонимание базового механизма.
3. **"hasOwnProperty и in делают одно и то же"** — нет. in ищет по всей цепочке, hasOwnProperty — только в самом объекте.
Прототипная цепочка вручную, затем то же самое через классы — показываем что это одно и то же
// ===== 1. ПРОТОТИПНАЯ ЦЕПОЧКА ВРУЧНУЮ =====
console.log('=== Ручные прототипы ===')
// Базовый объект
const Vehicle = {
type: 'транспортное средство',
describe() {
return `${this.brand || '?'} — ${this.type}`
},
toString() {
return `[${this.type}: ${this.brand || '?'}]`
}
}
// Создаём объект с Vehicle как прототипом
const Car = Object.create(Vehicle)
Car.type = 'автомобиль'
Car.drive = function() {
return `${this.brand} едет`
}
// Конкретный экземпляр
const tesla = Object.create(Car)
tesla.brand = 'Tesla'
tesla.model = 'Model S'
// Поиск по цепочке:
console.log(tesla.brand) // 'Tesla' — собственное
console.log(tesla.type) // 'автомобиль' — из Car
console.log(tesla.describe()) // 'Tesla — автомобиль' — из Vehicle
console.log(tesla.drive()) // 'Tesla едет' — из Car
// Цепочка прототипов
console.log('\n--- Цепочка прототипов ---')
let proto = tesla
let depth = 0
while (proto !== null) {
const props = Object.getOwnPropertyNames(proto)
.filter(k => k !== '__proto__')
console.log(`[${depth}] ${proto === tesla ? 'tesla' : proto === Car ? 'Car' : proto === Vehicle ? 'Vehicle' : 'Object.prototype'}: [${props.join(', ')}]`)
proto = Object.getPrototypeOf(proto)
depth++
}
// hasOwnProperty vs in
console.log('\n--- hasOwnProperty vs in ---')
console.log('brand - own?', tesla.hasOwnProperty('brand')) // true
console.log('type - own?', tesla.hasOwnProperty('type')) // false
console.log('describe - in?', 'describe' in tesla) // true
console.log('toString - in?', 'toString' in tesla) // true
// ===== 2. КЛАССЫ = ТЕ ЖЕ ПРОТОТИПЫ =====
console.log('\n=== Классы под капотом ===')
class Animal {
constructor(name, sound) {
this.name = name // собственное свойство
this.sound = sound // собственное свойство
}
speak() { // метод — на Animal.prototype
return `${this.name}: ${this.sound}!`
}
toString() {
return `Animal(name=${this.name})`
}
}
class Dog extends Animal {
constructor(name) {
super(name, 'Гав') // вызов Animal.constructor
this.tricks = [] // собственное свойство
}
learn(trick) { // метод — на Dog.prototype
this.tricks.push(trick)
return this
}
perform() {
return this.tricks.length > 0
? `${this.name} умеет: ${this.tricks.join(', ')}`
: `${this.name} ничему не обучен`
}
}
const rex = new Dog('Рекс')
rex.learn('сидеть').learn('лежать').learn('кувырок')
console.log(rex.speak()) // Рекс: Гав!
console.log(rex.perform()) // Рекс умеет: сидеть, лежать, кувырок
// Доказательство: это те же прототипы
console.log('\n--- Доказательство: class = прототипы ---')
console.log('typeof Animal:', typeof Animal) // function
console.log('rex.[[Prototype]] === Dog.prototype:',
Object.getPrototypeOf(rex) === Dog.prototype) // true
console.log('Dog.prototype.[[Prototype]] === Animal.prototype:',
Object.getPrototypeOf(Dog.prototype) === Animal.prototype) // true
console.log('Animal.prototype.[[Prototype]] === Object.prototype:',
Object.getPrototypeOf(Animal.prototype) === Object.prototype) // true
// Собственные свойства vs унаследованные
console.log('\n--- Собственные свойства rex ---')
console.log(Object.getOwnPropertyNames(rex)) // ['name', 'sound', 'tricks']
console.log('--- Методы на Dog.prototype ---')
console.log(Object.getOwnPropertyNames(Dog.prototype)) // ['constructor', 'learn', 'perform']
// instanceof проверяет цепочку прототипов
console.log('\n--- instanceof ---')
console.log('rex instanceof Dog:', rex instanceof Dog) // true
console.log('rex instanceof Animal:', rex instanceof Animal) // true
console.log('rex instanceof Object:', rex instanceof Object) // trueРеализуй иерархию наследования с помощью Object.create (без использования class). Создай объекты: Shape (базовый), Rectangle (наследует Shape) и Square (наследует Rectangle). Каждый должен иметь метод area() и describe().
Rectangle.area = function() { return this.width * this.height }. Rectangle.describe = function() { return "Прямоугольник " + this.width + "x" + this.height + ", площадь: " + this.area() }. createSquare: const sq = Object.create(Square); sq.side = sq.width = sq.height = side; sq.color = color.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке