Сегодня хорошо известно, что в 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}
Надеюсь, вы узнали что-то новое!