Ответ
Основное отличие ООП в JavaScript — прототипное наследование вместо классического. В JS объекты наследуют свойства и методы от других объектов через цепочку прототипов (__proto__), а не через классы (хотя синтаксис class добавлен в ES6 как синтаксический сахар).
Пример прототипного наследования:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise`);
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog('Rex');
dog.speak(); // Rex makes a noise
Ключевые отличия:
- Нет настоящих классов (до ES6)
- Динамическая природа — можно изменять прототипы и добавлять методы на лету
thisопределяется в момент вызова функции- Все объекты — ассоциативные массивы (ключ-значение)
- Множественное наследование сложно реализовать
Ответ 18+ 🔞
А, ну вот, опять про это ваше ООП в JavaScript. Слушай, тут такая хуйня, что мама не горюй. В других языках, типа Java, всё чинно-благородно: есть классы, ты от них наследуешься, и все довольны. А в JS, блядь, изначально была другая задумка, пиздец какая хитрая. Тут не классы, а прототипы, ёпта!
Представь себе: есть у тебя объект-папа. И другой объект говорит: «А дай-ка я у тебя всё спизжу!». И он не копирует, а просто ссылается на папины методы и свойства через какую-то магическую цепочку, которая называется __proto__. Это как если бы ты пришёл к соседу за солью, а он тебе говорит: «Да у меня нет, но у моего друга на третьем этаже есть, иди к нему». И ты идёшь по этой цепочке друзей, пока не найдёшь того, у кого есть соль, или пока не охуеешь от поисков.
Вот, смотри, как это выглядело раньше, до того как эти умники придумали синтаксис class (который, кстати, чистейший синтаксический сахар, под капотом та же самая прототипная хуйня):
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise`);
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = new Dog('Rex');
dog.speak(); // Rex makes a noise
Видишь эту дичь? Мы создаём функцию-конструктор Animal. Потом мы лезем в её prototype и пихаем туда метод speak. Потом мы хотим сделать собаку. Мы вызываем внутри Dog конструктор Animal через call, чтобы this.name установился. А потом — внимание, самый пиздец — мы говорим, что прототип Dog будет новым объектом, созданным на основе прототипа Animal. И ещё, блядь, не забываем поправить constructor, а то всё поломается, ёпта! В итоге собака Rex лает. Ну, в смысле, шумит. Охуенно, да?
И вот в чём, сука, основные отличия этой прототипной вакханалии:
- Настоящих классов не было, блядь, вообще! Пока в ES6 не добавили слово
class, чтобы все эти хипстеры успокоились. Но под капотом — всё та же прототипная цепочка, просто причёсанная. - Всё можно менять на лету, как перчатки! Захотел — добавил метод прямо в прототип, и он тут же появился у всех существующих объектов, которые от него наследуются. Это как если бы ты посреди спектакля начал менять декорации, а актёры бы даже не заметили. Мощно, но можно так наворотить, что сам потом не разберёшься.
this— это вообще отдельная песня, ядрёна вошь! Он определяется не там, где функция объявлена, а в момент её вызова. Можешь вызвать функцию в контексте одного объекта, а потом — другого. Гибко? Гибко. Запутанно? Овердохуища!- Все объекты — это по сути словари, ассоциативные массивы. Можешь добавлять, удалять свойства когда угодно. Хочешь, чтобы у твоего пользователя внезапно появилось свойство
любимый_цветок = "ромашка"— пожалуйста, на тебе. - Множественное наследование? Забудь, как страшный сон. Реализовать это на прототипах — это тот ещё геморрой. Приходится выкручиваться через миксины или композицию. В общем, адок.
Вот такая, блядь, философия. Не «я — экземпляр такого-то класса», а «я — объект, и у меня есть другой объект, на которого я ссылаюсь, если чего-то не знаю». Прикольно, мощно, но иногда хочется просто взять и ебнуть головой об клавиатуру.