Немного про ООП

ООП

ООП - Это парадигма прогроммирования расшифровывается как "Объектно-Ориентированное Программирование". Это наука о том как делать правильную архитектуру)
Парадигма программирования — это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером. Разные языки используют разные принципы внутри себя. Спасибо википедия!
Так вот javaScript - Объектно-Ориентированный язык и главную роль в нем играет объект. Этот объект внутри себя может содержать методы, свойства, любой тип данных и представлять собой целостную сущность

Типичный пример с авто

Представим себе легковую машину как объект, у него есть такие свойства как двигатель, кузов, колёса и тд. Есть такие методы как езда, перевозка чего либо и тд, все то, что авто умеет делать и эти методы у всех легковых авто одинаковы. Смысл объектно-Ориентированного программирования в том, что мы представляем любую вещь как объект с набором свойств, методов. Точно также мы можем представить и холодильник и микроволновку и так далее.

Как работают примитивы в js

Вот обычная строка 'люблю аниме' - это примитив. Но как только мы применяем какой то метод, то javaScript оборачивает эту строку в объект и после модификации возвращает строку на место.

// В этом примере new String() по сути и есть эта обертка, это буквальный пример.
let str = 'Люблю аниме';
let strObj = new String (str); // new String создает новую строку.
// на основании строки в str мы в strObj создаем новую строку.

console.log(typeof(str)); // string
console.log(typeof(strObj)); // object

Js просто оборачивает строку в объект, потом использует какой-то метод который есть у этого объекта, а потом возвращает все обратно. Каждый примитив имеет свой собственный объект обёртку: String, Number, Boolean и Symbol.
Отличный пример с learnJs:

let str = "Привет";

str.test = 5; // (*)
alert(str.test);
/* В зависимости от того, используете ли вы строгий режим (use strict) или нет, результат может быть:

undefined (без strict)
Ошибка (strict mode)
Почему? Давайте посмотрим что происходит в строке кода, отмеченной (*):

В момент обращения к свойству str создаётся «объект-обёртка».
В строгом режиме, попытка изменения этого объекта выдаёт ошибку.
Без строгого режима, операция продолжается, объект получает свойство test, но после этого он удаляется, так что на последней линии str больше не имеет свойства test.
Данный пример наглядно показывает, что примитивы не являются объектами. */

Прототипное наследование

Когда мы что-то создаем, мы создаем просто экземпляр прототипа. Посмотреть это можно через console.dir()

console.dir([1,2,3]); // в консоли браузера мы увидим __proto__:Array(0)
// если мы откроем эту вкладку, то увидим все методы которые могут применяться к массивам.
// Там же можно посмотреть прототип прототипа массива и это будет объект, все приходит к объекту

Создаем свой прототип

// это по сути наш прототип, от него будем создавать экземпляры
const soldier = {   // создаем объект и описываем его
    health:400,   // у нашего солдата будет 400хп
    armor:100,    // и 100 брони
    sayHello: function () {
        console.log('hello');
    }
};

const jonh = {     // наш экземпляр, у него те же свойства, но некоторые отличаются в виду его особеностей
    health: 100    // хп у него чутка меньше
};

Связываем экземпляр с прототипом

Устаревший способ

Теперь связываем jonh с нашим прототипом с помощью proto - это устаревший способ.

jonh.__proto__ = soldier; // обращаемся к свойству __proto__ и просто добавляем туда экземпляр.

console.log(jonh); // { health: 100 } В консоли показывает только его здоровье
// но если я сейчас обращусь к свойству armor через jhon
console.log(jonh.armor); // 100
//  У самого объекта только одно свойство, но благодаря тому, что мы указали прототип он так же получил и свойства прототипа!
// все это касается и методов
jonh.sayHello(); // hello

Современные способы

Object.setPrototypeOf()

Object.setPrototypeOf(jonh,  soldier); // Первый аргумент - это тот кому мы назначаем прототип
                                      // Второй - это сам прототип
jonh.sayHello(); // hello

Это все работа в динамике. Джон существовал и мы ему установили какой-то прототип, но обычно это все происходит на этапе создания объекта.

Object.create()

const jack = Object.create(soldier); // мы создаем объект jack и сразу назначили ему прототип!

jack.sayHello(); // hello

jack.health = 150;  // Все и дальше можем спокойно с ним работать

console.log(jack); //{ health: 150 }
console.log(jonh); // { health: 100 }
// два бравых солдата с разным хп, но одинаковой броней из за их общего прототипа

Если мы посмотрим на нашего солдата через console.dir() мы увидим от кого он наследуется.

console.dir(jonh);
alt text

В нашем случае, это некий объект со свойствами health:400, armor:100, и методом sayHello. Никого не напоминает?