Это сводка различных шаблонов в JS Object Creation и выполнения различных действий с этими объектами.

Для каждого из представленных ниже шаблонов мы хотим выполнить следующие функции:

  • Создание объекта или функции
  • Расширение объекта или функции
  • Создание подкласса со свойствами из нескольких объектов или функций

Заводской узор

Создание фабричной функции:

function Dog(name, age) {
  return {
    name,
    age,
    barks() {
      console.log(`${this.name} barks!`);
    }
  }
}
let menno = Dog(‘menno’, 12);
console.log(menno.name); // menno
console.log(menno.age); // 12
menno.barks(); // menno barks!

Расширение функции Dog:

function JackRussell(name, age) {
  return Object.assign(Dog(name, age),
    jumps() { console.log(`${this.name} jumps!`)
    },
  });
}
let bram = JackRusell(‘bram’, 10);
console.log(bram.name); // bram
console.log(bram.age); // 10
bram.barks(); // bram barks!
bram.jumps(); // bram jumps!

Сочетание свойств Джека Рассела и золотистого ретривера:

function GoldenRetriever(name, age) {
  return Object.assign(Dog(name, age), {
    swims() {
      console.log(`${this.name} swims!`);
    }
  })
}

Теперь создаем смесь GoldenRussell со свойствами Retriever и Jackie:

function GoldenRussell(name, age) {
  return Object.assign(JackRussell(), GoldenRetriever(name, age));
}
let bruno = GoldenRussell(‘bruno’, 1);
console.log(bruno.name); // bruno
console.log(bruno.age); // 1
bruno.barks(); // bruno barks!
bruno.jumps(); // bruno jumps!
bruno.swims(); // bruno jumps!

Шаблон конструктора

Создание функции-конструктора:

function Dog(name, age) {
  this.name = name;
  this.age = age;
  this.barks = function() {
    console.log(`${this.name} barks!`);
  }
}
let menno = new Dog(‘menno’, 11);
let luna = Object.create(menno);
menno.name;
menno.age;
menno.barks();

Альтернативно:

function Dog(name, age) {
  this.name = name;
  this.age = age;
}
Dog.prototype.barks = function() {
 console.log(`${this.name} barks!`)
}
function Husky() {}
Husky.prototype = new Dog(name, age);

Смешивание шаблона конструктора с OOLO должно привести к тому же результату:

function Dog() {};
Dog.prototype.barks = function() {
  console.log(‘it barks!’);
}
let jackie = new Dog();
let menno = Object.create(jackie);

Проверка прототипного наследования:

Dog.isPrototypeOf(jackie); // false
Dog.prototype.isPrototypeOf(jackie); // true
Dog.prototype.isPrototypeOf(menno); // true
jackie.isPrototypeOf(menno); // true
// Checking for use of instanceof
jackie instanceof Dog; // true
menno instanceof Dog; // true
menno instanceof jackie; // TypeError

Расширение функции Dog:

jackie.jumps = function() {
  console.log(‘it jumps!’)
}
Dog.prototype.swims = function() {
  console.log(‘it swims!’)
}
Object.defineProperties(jackie, {
  eats: {
    value: function() {
      console.log(‘it eats!’);
    },
    writable: true,
  },
})
jackie.__proto__.sleep = function() {
  console.log(‘it sleeps!’);
}
Object.getPrototypeOf(jackie).run = function() {
  console.log(‘it runs!’);
}
menno.jumps(); // it jumps!
menno.swims(); // it swims!
menno.eats(); // it eats!
menno.sleep(); // it sleeps!
menno.run(); // it runs!

Смешивание другими методами:

function Wolf() {
  this.hunts = function() {
    console.log(‘it hunts!’);
  }
}
let wolf = new Wolf();
function Husky() {
  return Object.assign(wolf, Dog.prototype);
}
let husky = new Husky();
husky;

Давайте воспользуемся наследованием, чтобы разделить методы между различными функциями конструктора.

function Dog(name, age) {
  this.name = name;
  this.age = age;
  this.barks = function() {
    console.log(‘it barks!’)
  }
}
function Husky(name, age) {
  Dog.call(this, name, age);
  this.hunts = function() {
    console.log(‘it hunts!’)
  }
}
let lars = new Husky(‘lars’, 8);
lars.barks(); // it barks!
lars.hunts(); // it hunts!
Object.getPrototypeOf(lars) === Husky.prototype; // true
Object.getPrototypeOf(lars) === Dog.prototype; // false
lars instanceof Husky; // true
lars instanceof Dog; // false

Обратите внимание, что после применения Dog.call() в новой функции Husky любые новые объекты Husky теперь имеют доступ к уже существующим объектам в классе Dog.

Но что, если мы добавим в Dog еще один метод?

let menno = new Dog(‘menno’, 11);
Dog.prototype.swims = function() {
  console.log(‘it swims!’);
}
menno.swims(); // it swims!
lars.swims(); // TypeError

Исправление должно гарантировать, что Husky наследуется от прототипа Dog.

Husky.prototype = Object.create (Dog.prototype);
Husky.prototype.constructor = Husky;
let lars = new Husky(‘lars’, 8);
lars.swims(); // it swims!

Таким образом, причина установки прототипной цепочки заключается в том, чтобы новый объект всегда мог наследовать от функции, а не просто получать доступ к методам, с помощью которых он был первоначально вызван (Dog.call()).

Шаблон ES6 с использованием класса

class Dog {
  constructor(name, age) {
    this.name = name;
    this.age = age
  };
  barks() {
    console.log(‘it barks!’);
  };
}
let menno = new Dog(‘menno’, 11);
menno.barks();
class Husky extends Dog {
  hunts() {
    console.log(‘it hunts!’);
  }
}
let lars = new Husky(‘lars’, 8);
lars.hunts();
Husky.prototype.isPrototypeOf(lars);
Dog.prototype.isPrototypeOf(lars);

Добавляя миксины из нескольких классов, Wolfcat получает свойства как от Husky, так и от Cat.

class Cat {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  };
  purrs() {
    console.log(this.name + ‘ purrs!’);
  };
  plays() {
    console.log(this.name + ‘ plays!’);
  };
};
class Wolfcat extends Husky {
  constructor(name, age) {
    super (name, age);
  }
}
for (let method of Object.getOwnPropertyNames(Cat.prototype).filter(item => typeof Cat.prototype[item] === ‘function’ && item !== ‘constructor’)) {
  Wolfcat.prototype[method] = Cat.prototype[method];
}
let feline = new Wolfcat(‘feline’, 2);
feline.barks(); // it barks!
feline.hunts(); // it hunts!
feline.purrs(); // it purrs!
feline instanceof Husky; // true
feline instanceof Wolfcat; // true
feline instanceof Cat; // false
Dog.prototype.isPrototypeOf(feline); // true
Husky.prototype.isPrototypeOf(feline); // true
Cat.prototype.isPrototypeOf(feline); // false
Object.getPrototypeOf(feline) === Wolfcat.prototype; // true

Шаблон OLOO

let Dog = {
  init: function(name, age) {
    this.name = name;
    this.age = age;
    return this;
  },
  barks() {
    console.log(‘it barks!’);
  }
}
let luna = Object.create(Dog).init(‘luna’, 8);
let Jackie = Object.create(Dog, {
  jumps: {
    value: function() {
      console.log(‘it jumps!’);
    },
    writable: false,
    enumerable: true,
  }
});
let menno = Object.create(Jackie).init(‘menno’, 11);
let Fish = {
  swims() {
    console.log(‘it swims!’);
  }
}
let Retriever = Object.assign(Object.create(Dog), Jackie, Fish);
let lulu = Object.create(Retriever).init(‘lulu’, 7);
lulu.swims(); // it swims
lulu.jumps(); // it jumps
lulu.barks(); // it barks