Данная статья рассматривает различные шаблоны для создания объектов в JavaScript и плюсы и минусы каждого из них.
Самым популярным способом создания объекта является использование синтаксиса литерала объекта:
let person = {
firstName: 'John',
lastName: 'Doe'
};
Однако синтаксис литерала объекта удобен для создания одного объекта. Если вы хотите создать несколько похожих объектов, вам нужно использовать один из следующих шаблонов:
- Фабрика (Factory pattern);
- Конструктор (Constructor pattern);
- Прототип (Prototype pattern);
- Конструктор-прототип (Constructor / prototype pattern);
- Паразитарный конструктор (Parasitic constructor pattern);
- Устойчивый конструктор (Durable constructor pattern);
Фабрика (Factory pattern)
В шаблоне фабрики используется функция, позволяющая абстрагироваться от процесса создания конкретных объектов.
Например, следующая функция createAnimal() инкапсулирует логику создания объекта животного.
function createAnimal(name) {
let o = new Object();
o.name = name;
o.identify = function() {
console.log('Привет! Я - ' + o.name);
}
return o;
}
Функция createAnimal() принимает аргумент, который будет использоваться для инициализации свойства name объекта животного.
Чтобы создать новый объект, вам просто нужно вызвать эту функцию и передать аргумент name следующим образом:
let cat = createAnimal('Кот');
let dog = createAnimal('Собака');
cat.identify(); // => Привет, Я - Кот
dog.identify(); // => Привет, Я - Собака
Хотя шаблон фабрики может создавать несколько похожих объектов, он не позволяет вам определить тип создаваемого объекта.
Конструктор (Constructor pattern)
JavaScript позволяет создавать настраиваемую функцию-конструктор, определяющую свойства и методы создаваемых пользователем объектов.
По соглашению, имя функции-конструктора в JavaScript начинается с заглавной буквы.
Следующий пример - как создать наш объект животного с помощью конструктора:
function Animal(name) {
this.name = name;
this.identify = function() {
console.log('Привет! Я - ' + this.name);
};
}
В отличие от фабрики, свойства и методы объекта animal назначаются непосредственно элементу this внутри функции-конструктора.
На этом этапе движок JavaScript создает функцию Animal() и анонимный объект.
Функция Animal() по-умолчанию содержит свойство prototype, ссылающееся на анонимный объект, а анонимный объект содержит свойство constructor, ссылающееся на функцию Animal(). И движок JavaScript связывает анонимный объект и наш Object.
console.log(Animal.prototype.constructor == Animal) // => true
Схематически это выглядит так:
Чтобы создать новый экземпляр Animal, нужно использовать оператор new. Например:
let dog = new Animal('Собака');
Вот что происходит под капотом JavaScript:
- Создаётся новый объект;
- Устанавливается значение this конструктора новому объекту;
- Выполняется код внутри конструктора, т.е. добавляются свойства к новому объекту;
- Возвращается новый объект.
Для нашего созданноо объекта dog:
console.log(dog.constructor === Animal); // => true
Поскольку объект dog не имеет свойства constructor, движок JavaScript следует по цепочке прототипов, чтобы найти его в объекте Animal.prototype.
Он нашел свойство constructor в объекте Animal.prototype, и в этом случае constructor указывает на функцию Animal(), поэтому приведенное выше утверждение возвращает true.
Объект dog является экземпляром как для Animal, так и для Object:
console.log(dog instanceof Animal); // => true
console.log(dog instanceof Object); // => true
Недостатком шаблона конструктора является то, что один и тот же метод identify() дублируется в каждом экземпляре.
Создадим ещё один объект Animal с именем cat:
let cat = new Animal('Кот');
Как видите, метод identify() дублируется как в объектах dog, так и в cat. Чтобы решить эту проблему, используйте шаблон прототипа.
Прототип (Prototype pattern)
Шаблон прототипа добавляет свойства объекта к прототипу объекта. Затем эти свойства доступны и используются всеми экземплярами.
В следующем примере шаблон прототипа используется, чтоб перезаписать объект выше:
function Animal() {
// свойства будем добавлять для prototype
}
Animal.prototype.name = 'Животное';
Animal.prototype.identify = function() {
console.log('Привет! Я - ' + this.name);
}
Теперь создадим экземпляр для Animal:
let dog = new Animal();
dog.name = 'Собака'; // переписываем свойство name
В строке dog.name = 'Собака'; движок JavaScript добавляет свойство name к объекту dog. В результате оба объекта dog и Animal.prototype имеют одинаковое свойство name.
Внутри метода identify() this ссылается на объект dog, поэтому this.name ссылается на свойство name объекта dog. В результате:
dog.identify(); // => Привет! Я - Собака
Попробуем удалить свойство name в объекте dog и вызовем identify():
delete dog.name;
dog.identify(); // => Привет! Я - Животное
JavaScript не может найти свойство name в объекте dog, поэтому он следует по цепочке прототипов и находит его в объекте Animal.prototype. Следовательно, this.name вернёт "Животное".
Конструктор-прототип (Constructor / prototype pattern)
Комбинация шаблонов конструктора и прототипа - наиболее распространенный способ определения пользовательских типов.
Шаблон конструктора определяет свойства объекта, а шаблон прототипа определяет методы и общие свойства.
При использовании такого паттерна все объекты настраиваемого типа совместно используют метод, и каждый из них имеет свои собственные свойства. Такой конструктор / прототип использует лучшие части шаблонов конструктора и прототипа.
function Animal(name) {
this.name = name;
}
Animal.prototype.identify = function() {
console.log('Привет! Я - ' + this.name);
}
let dog = new Animal('Собака');
dog.identify(); // => Привет! Я - Собака
let cat = new Animal('Кот')
cat.identify(); // => Привет! Я - Кот
Паразитарный конструктор (Parasitic constructor pattern)
В шаблоне паразитарного конструктора вы создаете функцию-конструктор, которая создает объект и возвращает этот объект.
function Animal(name) {
let o = new Object();
o.name = name;
o.identify = function() {
console.log('Привет! Я - ' + o.name);
}
return o;
}
В примере функция конструктора Animal такая же, как и в шаблоне фабрики. Однако вы вызываете её как конструктор с помощью оператора new.
var dog = new Animal('Собака');
По умолчанию оператор new возвращает объект, возвращаемый конструктором функции. Если constructor не возвращает объект, оператор new создает этот объект.
Устойчивый конструктор (Durable constructor pattern)
Устойчивый объект - это объект, не имеющий общедоступного свойства, а его методы не ссылаются на его this.
Устойчивые объекты часто используются в безопасных средах, где доступ к this и new запрещен.
Пример:
function secureAnimal(name) {
let o = new Object();
o.identify = function() {
console.log(name); // this не используем
}
return o;
}
let fox = secureAnimal('Лиса');
fox.identify(); // => Лиса
fox является устойчивым объектом, который не позволяет внешнему коду получать доступ к своим свойствам без вызова его методов.
Комментарии (0)