Сегодня хорошо известно, что в JavaScript, в отличие от многих других языков программирования высокого уровня, таких как Java и C#, объектно-ориентированное программирование реализуется с помощью механизма, называемого Прототипы.

Вместо канонической концепции объектов, являющихся экземплярами классов, объекты JS создаются с использованием существующих объектов,точнее, их функций-конструкторов. Как только объект создан из прототипа, свойства и методы прототипа становятся доступными для нового объекта, созданного из него. А если родитель-прототип создан с использованием другого объекта, то потомку доступны свойства и функции родителя-родителя. Это может продолжаться бесконечно в цепочке прототипов, пока мы не достигнем объекта, который имеет нуль в качестве прототипа.

Создание новых объектов

Простейший способ создания новых объектов — использование литеральной записи объекта:

const obj={}

Здесь переменная obj объявлена ​​и инициализирована с помощью пустого объекта. Однако в браузере видно, что она не пуста 'per se', но созданный с использованием глобального прототипа,также известного как Object.prototype, что позволяет получить доступ к свойствам и методам объекта.

Создание объектов с использованием функций конструктора объектов также приведет к тому же результату:

const obj=new Object()
const obj=Object()

даже передача null создаст тот же эффект:

const obj=new Object(null)

Поскольку Object является прототипом по умолчанию, использование его функций конструктора неизбежно установит себя в качестве прототипа.

Чтобы создать действительно новый объект без наследия, нам нужно использовать:

const empty=Object.create(null)

Функция Object.create создает новый объект и назначает прототип объекта, используя в качестве существующего объекта, который мы ей передаем.

В качестве примера предположим, что мы определили объект с именем building, инициализированный некоторыми реквизитами и функциями:

const building = {
address: "John Av. 35",
rooms: 5,
isCleaned: false,
residents: ["David", "Alex", "Sara", "Jill"],
setClean: function(){
this.isCleane = true;
console.log("Building cleaned");
},
setNotClean: function(){
this.isClean = false;
console.log("Building is not clean");
},
};

👉 Обратите внимание, что я использую не стрелочные функции, а анонимные операторы функций, поэтому ключевое слово this будет указывать на сам объект здание. В противном случае запуск building.setClean() установит новое свойство isClean в глобальный объект окна и инициализирует его значением true. Попробуйте!

Теперь мы хотим использовать здание в качестве прототипа для создания из него нового объекта, дома.

Как видите, мы установили прототип нового объекта, дома, в значение здания объект. Поскольку объект-дом содержит ссылку на свой объект-прототип (через свойство __proto__), теперь он может получить доступ к своим свойствам и использовать их.

👉 Обратите внимание, что house не имеет собственных свойств. Мы можем установить их с помощью записи через точку следующим образом:

Мы также можем установить свойства House, передав их в качестве параметров функции Object.create:

const house=Object.create(building,{wallColor:{value:"white"},bathrooms:{value:2}})

Вы заметите, что объект, переданный в качестве второго параметра функции Object.create, не является обычным, поскольку он описываетсвойства, которые вы хотите добавить:

Прототипы НЕ являются неизменными

Что, если мы хотим изменить унаследованное имущество, скажем, в нашем доме всего 3 комнаты, а не 5?

Как видите, изменять значения безопасно, так как попытка внесения изменений только добавляла новое свойство с тем же ключом, содержащим новое значение. Прототип не пострадал.

Тем не менее, порототип не является неизменным, поэтому доступ к его свойствам через __proto__ опасен и фактически внесет изменения:

Но это просто плохая практика. Не делайте этого.

Конструктор объектов

Как мы знаем, другим способом создания новых объектов является использование функции-конструктора или оператора new, который вызывает функцию-конструктор:

const house=Object(building) 
//or
const house_sec=new Object(building)

Если вы внимательно посмотрите, то заметите, что возвращенный объект выглядит точно так же, как исходный объектздания.

Прямой прототип по-прежнему является глобальным Объектом, а не зданием. Это связано с тем, что конструктор Object создает новый объект из переданного нами и возвращает его значение. Когда мы передали building, мы фактически передали объект типа Object, поэтому в итоге просто сделали копию. Это не то, чего мы хотели. Чтобы сделать копию, мы могли бы также использовать литералы объектов и оператор распространения:

const buildingCopy={...building}

Надеюсь, вы узнали что-то новое!