ООП в прототипном стиле

Prototype

prototype
 
function Son (own) {
  this.own = own;
}

Son.prototype
   .outer = 'Hi';

Son.toString()
method of Object

Son instanceof Object
true

typeof Object
function
                

Шаблон использования

общий метод
 
function Family(name) {
    this.name = name;
}
Family.prototype.getName = function () {
    return this.name;
}

const first = new Family('First')
const second = new Family('Second')
first.getName()
First
second.getName()
Second
            
общее свойство
 
function Child (name) {
    this.name = name;
}

Child.prototype.lastname = 'Simpson';

const bart = new Child('Bart');
const lise = new Child('Lise');
bart.lastname;
 Simpson
lise.lastname;
 Simpson
            
общее свойство

Собственные и наследуемые свойства объекта

hasOwnProperty() - вернет true если свойство объекта собственное

in - вернет true если свойство объекта присутствует среди собственных или в прототипах

in vs hasOwnProperty
 
function Family (name) {
    this.name = name;
}
Family.prototype.lastname = 'Simpson';

const bart = new Family('Bart');

const keys = [];
for( let key in bart) {
    keys.push(key)
}
[name, lastname]

const ownKeys = [];
for( let key in bart) {
    if(bart.hasOwnProperty(key))
    ownKeys.push(key)
}
[name]
            
Object.keys
 
function Family (name) {
    this.name = name;
}
Family.prototype.lastname = 'Simpson';

const bart = new Family('Bart');
const result = Object.keys(bart);
[name]
            

Расширенный синтаксис для задания свойств объекту

Object.defineProperty(obj, prop, descriptor) задает новое или переопределяет имеющееся свойство

Этот метод позволяет настроить дополнительные детали свойства:

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

const bart = new Family('Bart');
Object.defineProperty(bart, 'lastname', {
    enumerable: true,
    configurable: true,
    writable: true,
    value: 'Simpson'
  })

const result = Object.entries(bart);
[[name,Bart],[lastname,Simpson]]
            

Функция-конструктор

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


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

Son.prototype.lastname = 'Simpson';
Son.prototype.getFullName = function () {
    return `
 name is ${this.name}
 lastname is ${this.lastname}
 `;
}

const bart = new Son('Bart');
bart.getFullName();
name is Bart
lastname is Simpson

            

[[Prototype]] объекта

[[Prototype]]
[[Prototype]]

__proto__

[[Prototype]] объекта указывает на prototype функции-констркутора и даже может изменить его с помощью методов, находящихся в собственном свойстве __proto__

__proto__
свойство __proto__ можно использовать как указатель
 
function Son (name) {
    this.name = name;
}
const bart = new Son('Bart');
bart.__proto__

Не рекомендуется использовать метод __proto__ для изменения prototype

Объект вместо функции-конструктора

Если есть объект, который подходит в качестве основы, то мы можем сделать его копию и добавить новые свойства и методы с помощью Object.create(source, options)

Объект-прототип
 
function Child (name) {
    this.name = name;
}

Child.prototype.lastname = 'Simpson';

const bart = new Child('Bart');
const lise = Object.create(bart);
lise.name = 'Lise';
Lise
lise.lastname;
Simpson
lise instanceof Child;
true
            

Важно запомнить, что options не обязательный параметр, но если он используется, то с использованием синтаксиса Object.defineProperties()

Использование optrions
 
function Child (name) {
    this.name = name;
}

Child.prototype.lastname = 'Simpson';

const bart = new Child('Bart');
const lise = Object.create(bart,(
 {
    name: {
      value: 'Lise',
      writable: true
    }
 }
));
lise.name;
Lise
lise.lastname;
Simpson
lise instanceof Child;
true
            
Передача свойств другого объекта при копировании
 
function Child (name) {
    this.name = name;
}

Child.prototype.lastname = 'Simpson';

const bart = new Child('Bart');
bart.parent = ['Homer', 'Marge'];

const lise = Object.create(bart);
lise.name = 'Lise';
lise.parent
['Homer', 'Marge']
            

Такой способ позволяет использовать prototype общего конструктора.

Так же этот способ позволяет создать наследование, подобное классическому (от слова класс)

Наследование

Пример CSS наследования

Пример как это могло бы работать в JS

Реализация в JS
 
function Family () {}

Family.prototype.color = 'red';
const grand = new Family ();

const father = Object.create(grand)
const son = Object.create(father)
console.table([
    ['grand:', grand.color],
    ['father:',father.color],
    ['son:', son.color]
]);


father.color = 'orange';
console.log('============= father.color = "orange" ================');
console.table([
    ['grand:', grand.color],
    ['father:',father.color],
    ['son:', son.color]
]);

console.log('============= son.color = "navy" ================');
son.color= 'navy';
console.table([
    ['grand:', grand.color],
    ['father:',father.color],
    ['son:', son.color]
]);

            

Аналог классического наследования:

Наследование конструктора - apply(this, arguments) или .call()


function Family (name, age) {
    this.name = name,
    this.age = age
}

function Son (name, age) {
    Family.apply(this, arguments)
}

function Father (name, age) {
    Family.call(this, name, age)
}

const homer = new Father ('Homer', 36)
const bart = new Son ('Bart', 10)
console.table({homer, bart})
            

Наследование prototype и переопределение унаследованного constructor на собственный


function Family () {}
Family.prototype.lastname = "Simpson";

function Son () {}
Son.prototype = Object.create(Family.prototype);
Son.prototype.constructor = Son;
            

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

Пример в файле /example-js/inheritace.js

Результат в файле /example-js/res.txt

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

find property in chain

Алгоритм поиска

  1. в собственных свойствах объекта
  2. в свойствах prototype
  3. в свойствах prototype в цепочке prototype
  4. в Object.prototype
  5. переход поиска в null с последующим возвратом undefined