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

Для чего нужен Proto?

1.7 Middle🔥 121 комментариев
#Soft Skills и рабочие процессы

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

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

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

Для чего нужен Proto ([[Prototype]])

Краткий ответ

[[Prototype]] (или __proto__ в браузерах) — это механизм прототипного наследования в JavaScript. Каждый объект имеет скрытую ссылку на другой объект (его прототип), из которого он наследует свойства и методы. Это основа всей объектно-ориентированной модели в JavaScript.

Цель Proto: наследование и переиспользование кода

// Без прототипов пришлось бы копировать методы каждому объекту
const dog1 = {
  name: 'Rex',
  bark: function() { console.log(this.name + ' barks'); }
};

const dog2 = {
  name: 'Max',
  bark: function() { console.log(this.name + ' barks'); } // Дублирование!
};

// С прототипами — методы общие
const dogPrototype = {
  bark() { console.log(this.name + ' barks'); }
};

const dog1 = Object.create(dogPrototype);
dog1.name = 'Rex';

const dog2 = Object.create(dogPrototype);
dog2.name = 'Max';

dog1.bark(); // Rex barks
dog2.bark(); // Max barks

Как работает цепочка прототипов (Prototype Chain)

Когда вы обращаетесь к свойству объекта, JavaScript ищет его так:

  1. В самом объекте
  2. В его прототипе ([[Prototype]])
  3. В прототипе прототипа
  4. И так далее, пока не найдёт или не достигнет null
const animal = {
  eat() { console.log('Eating'); }
};

const dog = Object.create(animal);
dog.bark = function() { console.log('Barking'); };

dog.bark(); // "Barking" — найдено в самом dog
dog.eat();  // "Eating" — найдено в прототипе (animal)
dog.toString(); // [object Object] — найдено выше в цепочке (Object.prototype)

Цепочка прототипов

dog --[[Prototype]]--> animal --[[Prototype]]--> Object.prototype --[[Prototype]]--> null

Это и есть цепочка прототипов.

Способы создания объектов с прототипами

1. Object.create() — явное установление прототипа

const parent = {
  greet() { console.log('Hello'); }
};

const child = Object.create(parent);
child.greet(); // "Hello"

console.log(child.hasOwnProperty('greet')); // false
console.log('greet' in child); // true — унаследовано

2. Конструкторские функции (старый способ)

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

Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound');
};

const dog = new Animal('Dog');
dog.speak(); // "Dog makes a sound"

// dog.[[Prototype]] === Animal.prototype

3. ES6 классы (синтаксический сахар над прототипами)

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a sound');
  }
}

class Dog extends Animal {
  speak() {
    console.log(this.name + ' barks');
  }
}

const dog = new Dog('Rex');
dog.speak(); // "Rex barks"

// Под капотом это всё ещё прототипное наследование

Доступ к прототипу

const parent = { x: 1 };
const child = Object.create(parent);
child.y = 2;

// Способы доступа к прототипу
console.log(Object.getPrototypeOf(child)); // { x: 1 }
console.log(child.__proto__); // { x: 1 } (не стандартно, но работает)

// Изменение прототипа (редко)
Object.setPrototypeOf(child, { x: 10 });

Практический пример: наследование

// Прототип для животных
const AnimalProto = {
  eat() { console.log(this.name + ' is eating'); },
  sleep() { console.log(this.name + ' is sleeping'); }
};

// Прототип для собак (наследует от животных)
const DogProto = Object.create(AnimalProto);
DogProto.bark = function() { console.log(this.name + ' is barking'); };

// Создание конкретной собаки
const myDog = Object.create(DogProto);
myDog.name = 'Buddy';

myDog.eat();   // "Buddy is eating" (от AnimalProto)
myDog.bark();  // "Buddy is barking" (от DogProto)
myDog.sleep(); // "Buddy is sleeping" (от AnimalProto)

Цепочка прототипов при ES6 классах

class Vehicle {
  move() { console.log('Moving'); }
}

class Car extends Vehicle {
  drive() { console.log('Driving'); }
}

const myCar = new Car();

// Цепочка:
// myCar --[[Prototype]]--> Car.prototype 
//        --[[Prototype]]--> Vehicle.prototype 
//        --[[Prototype]]--> Object.prototype 
//        --[[Prototype]]--> null

myCar.drive(); // "Driving" (в Car.prototype)
myCar.move();  // "Moving" (в Vehicle.prototype)
myCar.toString(); // "[object Object]" (в Object.prototype)

Важная разница: свойства vs методы

const proto = { x: 1 };
const obj = Object.create(proto);
obj.y = 2;

// Собственные свойства объекта
console.log(obj.hasOwnProperty('y')); // true
console.log(obj.hasOwnProperty('x')); // false

// Но доступны оба
console.log(obj.y); // 2
console.log(obj.x); // 1

// Когда меняем унаследованное свойство
obj.x = 10; // Создаёт своё свойство x, не меняет прототип
console.log(obj.x);              // 10 (своё)
console.log(proto.x);            // 1 (прототип не изменился)
console.log(obj.hasOwnProperty('x')); // true (теперь своё)

Часто встречающиеся ошибки

1. Путаница между прототипом и прототипом функции

function Dog() {}

// Dog.prototype — это объект, от которого наследуют экземпляры
Dog.prototype.bark = function() { console.log('Woof'); };

const dog = new Dog();
// dog.[[Prototype]] === Dog.prototype

// Dog.[[Prototype]] === Function.prototype (это другое!)

2. Изменение прототипа после создания объекта

const parent = { x: 1 };
const child = Object.create(parent);

parent.x = 2; // Меняем прототип
console.log(child.x); // 2 — изменилось! (смотрит на прототип)

child.x = 10; // Создаём своё свойство
parent.x = 20;
console.log(child.x); // 10 — своё свойство затеняет прототип

3. Performance: глубокие цепочки прототипов медленнее

// Плохо — очень глубокая цепочка
const a = { x: 1 };
const b = Object.create(a);
const c = Object.create(b);
const d = Object.create(c);
const e = Object.create(d);
const f = Object.create(e);

console.log(f.x); // JavaScript ищет x через 5 уровней прототипов

// Хорошо — используйте композицию или копирование для часто используемых методов

Вывод

  • [[Prototype]] — механизм наследования в JavaScript
  • Цепочка прототипов позволяет переиспользовать методы
  • Object.create() — явное создание объекта с прототипом
  • ES6 классы — удобный синтаксис поверх прототипов
  • hasOwnProperty() помогает различить собственные свойства от унаследованных
  • Не используйте __proto__ напрямую в production коде
  • Помните о цепочке прототипов при отладке
Для чего нужен Proto? | PrepBro