Оригинальный текст — https://addyosmani.com/
Паттерны проектирования Javascript
Разработчики частенько задаются вопросом, существует ли идеальный паттерн или набор паттернов, которые они должны использовать в рабочем процессе. Хорошего ответа на этот вопрос не существует. Каждый сценарий и веб-приложение, над которым мы работаем, скорее всего, будут иметь свои собственные индивидуальные потребности, и нам нужно подумать о том, что какую ценность может предложить тот или иной паттерн.
Например, некоторые проекты могут извлечь выгоду из паттерна Observer, в то время как другие могут просто быть слишком маленькими для его использования.
Тем не менее, как только программист разберется в шаблонах проектирования и проблемах, к которым они лучше всего подходят, будет гораздо проще интегрировать их в архитектуру приложений.
Паттерн Constructor
В классических объектно-ориентированных языках программирования конструктор представляет собой специальный метод, используемый для инициализации вновь созданного объекта. В JavaScript, почти все является объектом, поэтому нас должны очень интересовать конструкторы объектов.
Конструкторы объектов используются для создания определенных типов объектов. В них объект готовится к использованию, принимая для этого аргументы, которые конструктор может использовать для установки значений свойствам и методам элемента.
Создание объекта
Три варианта создания объекта в JavascriptЖ
1 2 3 4 5 6 7 8 9 |
// Каждый метод приводит к созданию нового пустого объекта: var newObject = {}; // или var newObject = Object.create(Object.prototype); // или var newObject = new Object(); |
Четыре способа, установки объекту ключа и значения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
// Подходы в ECMAScript 3 // 1. Точечный синтаксис // Установка свойства newObject.someKey = "Hello World"; // Получение свойства var value = newObject.someKey; // 2. Синтаксис квадратных скобок // Установка свойства newObject["someKey"] = "Hello World"; // Получение свойства var value = newObject["someKey"]; // Подходы в ECMAScript 5 // 3. Object.defineProperty // Установка свойства Object.defineProperty( newObject, "someKey", { value: "какое-то значение", writable: true, enumerable: true, configurable: true }); // Можно даже создать отдельную функцию ... var defineProp = function ( obj, key, value ){ var config = { value: value, writable: true, enumerable: true, configurable: true }; Object.defineProperty( obj, key, config ); }; //... для её применения потребуется создать новый объект ... var person = Object.create( Object.prototype ); //... а дальше мы просто добавим свойства ... defineProp( person, "car", "Delorean" ); defineProp( person, "dateOfBirth", "1981" ); defineProp( person, "hasBeard", false ); //... на выходе должны получить: Object {car: "Delorean", dateOfBirth: "1981", hasBeard: false} console.log(person); // 4. Object.defineProperties // Установка нескольких свойств Object.defineProperties( newObject, { "someKey": { value: "Hello World", writable: true }, "anotherKey": { value: "Foo bar", writable: false } }); // Для получения свойств в примерах 3 и 4 используем методы из пункта 1 и 2 |
Как мы увидим чуть позже, эти методы могут даже использоваться для наследования:
1 2 3 4 5 6 7 8 9 10 11 |
// Создаём новый объект driver на основе прототипа person var driver = Object.create( person ); // Устанавливаем новое свойство defineProp(driver, "topSpeed", "100mph"); // Получаем родительское свойство dateOfBirth из прототипа person console.log( driver.dateOfBirth ); // Получаем свойство, которые мы сами и установили console.log( driver.topSpeed ); |
Используем конструктор
Как мы знаем, JavaScript не поддерживает концепцию классов, но поддерживает специальные функции-конструкторы, которые работают с объектами. Вызвав в коде функцию-конструктор с ключевым словом new, мы говорим, чтобы функция вела себя как конструктор и создала новый объект с элементами, определенными этой функцией.
Внутри конструктора ключевое слово this ссылается на новый создаваемый объект (это очень важно понимать).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function Car( model, year, miles ) { this.model = model; this.year = year; this.miles = miles; this.toString = function () { return this.model + " has done " + this.miles + " miles"; }; } // Использование: // Создаём новые экземляры Car var civic = new Car( "Honda Civic", 2009, 20000 ); var mondeo = new Car( "Ford Mondeo", 2010, 5000 ); // Откройте консоль браузера и вы увидите результат метода toString console.log( civic.toString() ); console.log( mondeo.toString() ); |
Это достаточно простой пример использования паттерна. Но в нём есть проблема: один и тот же метод toString будет определен буквально у каждого нового объекта Car, что не является хорошей практикой. Нужно чтобы все объекты ссылались на один метод.
Мы можем обойти эти проблемы следующим паттерном:
Паттерн Constructor с использованием прототипов
Функции, как и все объекты в JavaScript, содержат объект «прототип». Когда мы вызываем конструктор JavaScript для создания объекта, все свойства прототипа конструктора cтановятся доступными для нового объекта. Таким образом, могут быть созданы несколько объектов автомобилей, которые получают доступ к одному и тому же прототипу. Таким образом, мы можем расширить исходный пример следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function Car( model, year, miles ) { this.model = model; this.year = year; this.miles = miles; } // Обратите внимание, что теперь мы добавляем метод в прототип Car, // что позволяет избежать переопределения прототипа объекта Car.prototype.toString = function () { return this.model + " has done " + this.miles + " miles"; }; // Использование: var civic = new Car( "Honda Civic", 2009, 20000 ); var mondeo = new Car( "Ford Mondeo", 2010, 5000 ); console.log( civic.toString() ); console.log( mondeo.toString() ); |
Метод toString () теперь будет разделяться между всеми объектами Car.