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

Возможно ли отобразить Web страницу внутри Web страницы?

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

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

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

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

Что такое прототип и прототипальное наследование

Прототип — это механизм, на котором основана вся объектная система JavaScript. Это может быть сложным концептом, но он критически важен для понимания языка.

Что такое прототип

Определение

Каждый объект в JavaScript имеет скрытое свойство [[Prototype]] (внутреннее свойство), которое указывает на другой объект — его прототип. Когда ты ищешь свойство на объекте, JavaScript сначала ищет на самом объекте, потом на его прототипе, потом на прототипе прототипа, и так до конца цепи.

// У каждого объекта есть [[Prototype]]
const obj = {};
console.log(Object.getPrototypeOf(obj)); // Object {} - это базовый прототип

// Или через __proto__ (не рекомендуется в продакшене)
console.log(obj.__proto__); // То же самое

Цепь прототипов (Prototype Chain)

const animal = {
  sound: 'generic sound',
  makeSound() {
    console.log(this.sound);
  }
};

const dog = Object.create(animal);
dog.sound = 'woof';

dog.makeSound(); // 'woof'
console.log(dog.hasOwnProperty('sound')); // true
console.log(dog.hasOwnProperty('makeSound')); // false (на прототипе)

// Цепь: dog -> animal -> Object.prototype -> null

Как работает поиск свойства

Поиск идет по цепи

const grandParent = { level: 'grand' };
const parent = Object.create(grandParent);
parent.level = 'parent';

const child = Object.create(parent);
// child.level не определен

console.log(child.level); // 'parent' (найдено на parent)
console.log(child.age); // undefined (не найдено нигде)

// Процесс:
// 1. Есть ли child.level? Нет
// 2. Есть ли Object.getPrototypeOf(child).level? Да! 'parent'
// 3. Возвращаем 'parent'

hasOwnProperty vs in

const parent = { name: 'Parent' };
const child = Object.create(parent);
child.age = 5;

console.log(child.hasOwnProperty('age')); // true (прямое свойство)
console.log(child.hasOwnProperty('name')); // false (на прототипе)

console.log('age' in child); // true (прямое свойство)
console.log('name' in child); // true (включая прототип!)

Функции-конструкторы и новый способ (классы)

Старый способ: функции-конструкторы

function Animal(name) {
  this.name = name;
}

// Свойства добавляются конструктору
Animal.prototype.sound = 'generic';
Animal.prototype.makeSound = function() {
  console.log(`${this.name} makes ${this.sound}`);
};

const dog = new Animal('Шарик');
dog.sound = 'woof';

dog.makeSound(); // 'Шарик makes woof'

// Цепь: dog -> Animal.prototype -> Object.prototype -> null

Как работает new

// new делает вот что:
function Person(name) {
  // 1. Создает новый объект
  // const obj = {};
  
  // 2. Устанавливает [[Prototype]] на Person.prototype
  // Object.setPrototypeOf(obj, Person.prototype);
  
  // 3. Вызывает функцию с this = obj
  this.name = name; // obj.name = 'Иван'
  
  // 4. Возвращает obj (если не вернул другой объект)
  // return obj;
}

const person = new Person('Иван');
console.log(person.name); // 'Иван'
console.log(Object.getPrototypeOf(person) === Person.prototype); // true

Современный способ: классы (синтаксический сахар)

// Классы — это просто синтаксис для функций-конструкторов
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  makeSound() {
    console.log(`${this.name} makes sound`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  makeSound() {
    console.log(`${this.name} barks`);
  }
}

const dog = new Dog('Шарик', 'Овчарка');
dog.makeSound(); // 'Шарик barks'

// Под капотом это то же самое что функции-конструкторы!
// Только удобнее и понятнее

Наследование через прототипы

Способ 1: Object.create (рекомендуется)

const parent = {
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

const child = Object.create(parent);
child.name = 'Child';

console.log(child.greet()); // 'Hello, I'm Child'

Способ 2: Классы (современный)

class Parent {
  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

class Child extends Parent {
  constructor(name) {
    super();
    this.name = name;
  }
}

const child = new Child('Иван');
console.log(child.greet()); // 'Hello, I'm Иван'

Способ 3: Старый способ (не используй)

function Parent() {}
Parent.prototype.greet = function() {
  return `Hello, I'm ${this.name}`;
};

function Child(name) {
  this.name = name;
}

// Неправильно!
// Child.prototype = Parent.prototype; // Они будут одно и то же!

// Правильно:
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // Восстанавливаем constructor

const child = new Child('Иван');
console.log(child.greet()); // 'Hello, I'm Иван'

Переопределение методов (Override)

class Animal {
  sound() {
    return 'generic sound';
  }
}

class Dog extends Animal {
  sound() {
    return 'woof'; // Переопределяем
  }
}

class Cat extends Animal {
  sound() {
    return 'meow';
  }
}

const dog = new Dog();
const cat = new Cat();
const animal = new Animal();

console.log(dog.sound()); // 'woof'
console.log(cat.sound()); // 'meow'
console.log(animal.sound()); // 'generic sound'

instanceof работает через прототипы

class Animal {}
class Dog extends Animal {}

const dog = new Dog();

console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true (через цепь!)
console.log(dog instanceof Object); // true

// instanceof проверяет:
// Dog.prototype в цепи dog
// Animal.prototype в цепи dog
// Object.prototype в цепи dog

Свойства vs методы

class Counter {
  constructor(start = 0) {
    this.value = start; // Свойство
  }
  
  increment() { // Метод
    this.value++;
  }
}

const c = new Counter(5);
console.log(c.hasOwnProperty('value')); // true (свойство объекта)
console.log(c.hasOwnProperty('increment')); // false (на прототипе)
console.log('increment' in c); // true (найдено в цепи)

Производительность

Доступ к свойствам медленнее, чем методам

// Быстро: метод на прототипе (одно поднятие)
class Foo {
  method() { return 42; }
}

// Медленнее: каждый объект имеет свою функцию
function Bar() {
  this.method = () => 42;
}

// Что дольше ищется:
obj.method(); // 1 шаг (на прототипе)
obj.property; // 1 шаг (на объекте) или несколько (в цепи)

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

class Vehicle {
  constructor(brand) {
    this.brand = brand;
  }
  
  describe() {
    return `Марка: ${this.brand}`;
  }
}

class Car extends Vehicle {
  constructor(brand, model) {
    super(brand);
    this.model = model;
  }
  
  describe() {
    return super.describe() + `, Модель: ${this.model}`;
  }
}

class Tesla extends Car {
  constructor(model) {
    super('Tesla', model);
  }
  
  describe() {
    return super.describe() + ' (Электро)';
  }
}

const myTesla = new Tesla('Model 3');
console.log(myTesla.describe());
// 'Марка: Tesla, Модель: Model 3 (Электро)'

// Цепь: myTesla -> Tesla -> Car -> Vehicle -> Object

Заключение

Прототипы — это фундамент JavaScript:

  • Каждый объект имеет [[Prototype]]
  • Поиск свойства идет по цепи прототипов
  • Классы — это синтаксис для прототипального наследования
  • Используй классы в современном коде, они понятнее
  • Понимание прототипов помогает избежать ошибок с this, new и наследованием