Телевизоры. Приставки. Проекторы и аксессуары. Технологии. Цифровое ТВ

JavaScript — шаблоны наследования. Понимание шаблонов проектирования в JavaScript Создание шаблона модуль

В этой статье мы расскажем об общих шаблонах проектирования в JS. Эти шаблоны предлагают разработчикам способы решения технических проблем многоразовыми и элегантными способами. Хотите улучшить ваши JavaScript навыки? Тогда читайте дальше.

Что такое шаблон проектрирования или паттерн?

Проще говоря, дизайн паттерн представляет собой многократно используемое программное решение для определенного типа проблемы, которая часто возникает при разработке программного обеспечения. За много лет практики разработки ПО, эксперты выяснили пути решения подобных проблем. Эти решения были инкапсулированы в шаблоны дизайна.

  • шаблоны - это проверенное решения проблем разработки программного обеспечения
  • шаблоны являются масштабируемыми, поскольку они обычно структурированы и имеют правила, которых вы должны придерживаться
  • шаблоны могут быть повторно использованы для аналогичных задач

Разновидности шаблонов проектирования

При разработке программного обеспечения, дизайн паттерны, как правило, сгруппированы в несколько категорий. В этой статье, мы рассмотрим три наиболее важных.

Вкратце о них:

  • Порождающие шаблоны (creational patterns) сосредоточены на способах создания объектов или классов. Это может показаться простым (и это в некоторых случаях оно так и есть), но большие приложения должны контролировать процесс создания объекта.
  • Структурные шаблоны (structural design patterns) сосредоточены на том, чтобы управлять отношениями между объектами так, чтобы ваше приложение было построено масштабируемым способом. Ключевым аспектом структурной модели является обеспечение того, что изменение в одной части приложения не влияет на все другие части.
  • Поведенческие шаблоны (behavioral patterns) сосредоточены на связи между объектами
  • Примечание о классах в JavaScript

    Читая о дизайн шаблонах, вы часто будете видеть ссылки на классы и объекты. Это может привести к путанице, поскольку JavaScript на самом деле не использует “class” (класс), более правильным является термин “data type” (тип данных).

    Типы данных в JavaScript

    JavaScript является объектно-ориентированным языком, где объекты наследуют от других объектов, в концепции известной как прототипное наследство. Типы данных (data types) могут быть созданы путем определения того, что называется “функцией конструктора”, например:

    Function Person(config) { this.name = config.name; this.age = config.age; } Person.prototype.getAge = function() { return this.age; }; var tilo = new Person({name:"Tilo", age:23 }); console.log(tilo.getAge());

    Обратите внимание на использование prototype при определении методов на Person типе данных. Так как несколько Person объектов будут ссылаться на тот же прототип, это позволит getAge() методу быть разделенным всеми экземплярами Person типа данных, нежели его переопределения для каждого экземпляра. Кроме того, любой тип данных, который наследует от Person, будет иметь доступ к методу getAge().

    Работа с конфиденциальностью

    Другой распространенной проблемой в JavaScript является то, что у него нет никаких приватных переменных. Но, мы можем использовать несколько замыканий для имитации конфиденциальности. Рассмотрим следующий сниппет:

    Var retinaMacbook = (function() { //Приватные переменные var RAM, addRAM; RAM = 4; //Приватные методы addRAM = function (additionalRAM) { RAM += additionalRAM; }; return { //Публичные переменные и методы USB: undefined, insertUSB: function (device) { this.USB = device; }, removeUSB: function () { var device = this.USB; this.USB = undefined; return device; } }; })();

    В приведенном выше примере, мы создали retinaMacbook объект, с публичными и приватными переменными и методами. Вот, как мы будем это использовать:

    RetinaMacbook.insertUSB("myUSB"); console.log(retinaMacbook.USB); //logs out "myUSB" console.log(retinaMacbook.RAM) //logs out undefined

    Порождающие шаблоны

    Существует много различных видов порождающих шаблонов (абстрактная фабрика, строитель, фабричный метод, отложенная инициализация, пул одиночек, объектный пул, прототип, получение ресурса есть инициализация, одиночка), но в этом уроке, мы только рассмотрим два из них: Строитель (Builder) и Прототип(Prototype). Они используются достаточно часто, чтобы заработать к себе внимание.

    Строительный шаблон (Builder pattern)

    Строительный шаблон часто используется в веб-разработке, и вы, наверняка, использовали его раньше не осознавая этого. Проще говоря, этот шаблон может быть определен следующим образом:

    Применение строительного шаблона позволяет строить объекты, указав только тип и содержание объекта. Нет необходимости создания объекта.

    Например, вы, наверняка, делали это бесчисленное количество раз в jQuery:

    Var myDiv = $("This is a div."); //myDiv теперь представляет jQuery объект ссылающейся на DOM узел. var someText = $("

    "); //someText это jQuery объект ссылающейся на HTMLParagraphElement var input = $("");

    Взгляните на три примера выше. В первом из них, мы прошли в элемент с некоторым контентом. Во втором, мы прошли в пустой тег

    В последнем, мы прошли в элемент. Результат всех трех, был одинаковым, - нам был возвращен jQuery объект, ссылающийся на узел DOM.

    Переменная $ адаптирует строительный шаблон в jQuery. В каждом примере, нам был возвращен jQuery DOM объекти и имелся доступ ко всем методам, предоставляемых библиотекой jQuery, и не в одном из моментов, мы не вызывали document.createElement. JS library обработала все это за закрытыми дверями.

    Представьте себе, как много было бы работы, если нам пришлось бы создавать элемент DOM и вставлять содержимое в него! Используя строительный шаблон, мы можем сосредоточить свое внимание на типе и содержание объекта, а не на его явном создании.

    Прототипный шаблон (Prototype pattern)

    Ранее мы рассмотрели процесс по определению типов данных в JavaScript через функции и добавления методов в прототип объекта. Прототипные шаблоны (схемы), позволяют объектам наследовать от других объектов, через их прототипы.

    Прототипный шаблон - это шаблон, в котором объекты создаются на основе шаблона существующего объекта путем клонирования.

    Это простой и естественный способ реализации наследования в JavaScript. Например:

    Var Person = { numFeet: 2, numHeads: 1, numHands:2 }; //Object.create берет свой ​​первый аргумент и применяет его к прототипу нового объекта. var tilo = Object.create(Person); console.log(tilo.numHeads); //результат 1 tilo.numHeads = 2; console.log(tilo.numHeads) //результат 2

    Свойства (и методы) в Person объекте, применяются к прототипу объекта tilo. Мы можем переопределить свойства объекта tilo, если хотим, чтобы они были разными.

    В приведенном выше примере, мы использовали Object.create (). Однако, Internet Explorer 8 не поддерживает новый метод. В этих случаях мы можем имитировать его поведение:

    Var vehiclePrototype = { init: function (carModel) { this.model = carModel; }, getModel: function () { console.log("The model of this vehicle is " + this.model); } }; function vehicle (model) { function F() {}; F.prototype = vehiclePrototype; var f = new F(); f.init(model); return f; } var car = vehicle("Ford Escort"); car.getModel();

    Структурные шаблоны

    Структурные шаблоны действительно полезны, особенно при выяснении, как система должна работать. Они позволяют приложениям легко масштабироваться и оставаться управляемыми. Из этой немалой группы (адаптер, мост, компоновщик, декоратор, фасад, единая точка входа, приспособленец, заместитель), мы рассмотрим следующие модели: Компоновщик и Фасад.

    Компоновщик (шаблон проектирования)

    • Шаблон Компоновщик, - это ещё один вид шаблона, который вы, вероятно, использовали без осознания того.

    Итак, что же это значит? Давайте, рассмотрим следующий пример в jQuery (у большинства JS библиотек будут эквиваленты этому):

    $(".myList").addClass("selected"); $("#myItem").addClass("selected"); //Не делайте этого на больших таблицах, это всего лишь пример. $("#dataTable tbody tr").on("click", function(event){ alert($(this).text()); }); $("#myButton").on("click", function(event) { alert("Clicked."); });

    Большинство библиотек JavaScript обеспечивают последовательное API, независимо от того, имеем мы дело с одним элементом DOM или массивом DOM элементов. В первом примере, мы можем добавить selected класс ко все элементам подобраных селектором.myList, но мы также можем использовать этот же метод, когда речь идет об еденичном DOM элементе, #myItem. Точно так же можно приложить обработчик событий с помощью on() метода на нескольких узлах, или на одном узле через тот же API.

    Благодаря использованию композитных макетов, jQuery (и многие другие библиотеки) предоставляют нам упрощенный API.

    Composite шаблон иногда может вызывать проблемы. В слабо расписанном языке, таком как JavaScript, полезным будет знать, имеем ли мы дело с одним элементом или несколькими элементами. Так как компоновщик шаблон использует одинаковый API для обоих, мы зачастую можем принять одно за другое и в конечном итоге столкнуться с неожиданной ошибкой. Некоторым библиотекам, таким как YUI3, предлагают два отдельных метода получения элементов (Y.one() vs Y.all()).

    Фасад (шаблон проектирования)

    Вот еще один паттерн, который мы воспринимаем как должное. Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.

    Фасад паттерн предоставляет пользователю простой интерфейс, скрыв его основные сложности.

    Фасад шаблон, почти всегда улучшает удобство использования части программного обеспечения. Использование jQuery в качестве примера, одним из наиболее распространенных методов библиотеки, является ready() метод:

    $(document).ready(function() { //весь ваш код идет сюда... });

    Метод ready() фактически реализует фасад. Если взглянуть на источник, вот что вы найдете:

    Ready: (function() { ... //Mozilla, Opera, и Webkit if (document.addEventListener) { document.addEventListener("DOMContentLoaded", idempotent_fn, false); ... } //IE модель событий else if (document.attachEvent) { // обеспечьте firing до onload; может быть поздно, но безопасно для iframes document.attachEvent("onreadystatechange", idempotent_fn); // Резерв для window.onload, который всегда работает window.attachEvent("onload", idempotent_fn); ... } })

    Метод ready() не такой уж простой. jQuery нормализует непостоянство браузера, чтобы ready() сработал в нужный момент. Однако, как разработчик, вы будете представлены с простым интерфейсом.

    Большинство примеров фасад шаблонов, следует этому принципу. Для его реализации, мы обычно полагаемся на условные операторы, но представляем его в виде простого интерфейса для пользователя. Другие методы реализации этого паттерна включают в себя animate() и css().

    Поведенческие шаблоны

    Любая объектно-ориентированная система программного обеспечения будет иметь связи между объектами. Не организация таких связей может привести к ошибкам, которые трудно найти и исправить. Поведенческие шаблоны проектирования преписывают различные методы организации связи между объектами (цепочка ответственности, команда, интерпретатор, итератор, посредник, хранитель, наблюдатель, слуга, спецификация, состояние, стратегия, шаблонный метод, посетитель и т.д.). В этом раздел, мы рассмотрим Наблюдатель (Observer) и Посредник (Mediator) паттерны.

    Шаблон Наблюдатель (Observer)

    Вот что говорится о Наблюдателе:

    В паттерне наблюдатель, объект может иметь список наблюдателей, которые заинтересованы в его жизненном цикле. В любой момент, когда объект делает что-то интересное, он посылает уведомление своим наблюдателям. Если наблюдатель больше не заинтересован в прослушивание объекта, тогда он может удалить его из своего списка.

    Нам нужно три метода для описания этого паттерна:

    publish(data): вызывается объектом, когда у него есть уведомление. Некоторые данные могут быть переданы с помощью этого метода.
    subscribe(observer): вызывается объектом, для добавления наблюдателя в свой список наблюдателей.
    unsubscribe(observer): вызывается объектом, чтобы удалить наблюдателя из списка наблюдателей.
    Большинство современных JavaScript библиотек поддерживают эти три метода, как часть своей инфраструктуры событий. Обычно есть on() или attach() метод, trigger() или fire() метод, и off() или detach() метод. Рассмотрим следующий сниппет:

    //Мы просто создаем связь между методами jQuery событий var o = $({}); $.subscribe = o.on.bind(o); $.unsubscribe = o.off.bind(o); $.publish = o.trigger.bind(o); // Usage document.on("tweetsReceived", function(tweets) { //perform some actions, then fire an event $.publish("tweetsShow", tweets); }); //Мы можем subscribe к этому событию, а затем fire наше собственное событие. $.subscribe("tweetsShow", function() { //display the tweets somehow .. //publish после того, как оно показано $.publish("tweetsDisplayed); }); $.subscribe("tweetsDisplayed, function() { ... });

    Паттерн наблюдатель является одним из самых простых шаблонов по реализации, и очень мощным. JavaScript хорошо подходит для принятия этого шаблона, поскольку он сам по себе основан на событиях. В следующий раз при разработки веб-приложений, задумайтесь о разработке модулей, которые слабо связанны друг с другом и принимают паттерн Наблюдатель в качестве средства связи. Паттерн наблюдатель может стать проблематичным, если участвуют слишком много предметов и наблюдателей.

    Шаблон Посредник (Mediator)

    Последний паттерн, который мы рассмотрим, это Посредник. Он похож на паттерн Наблюдатель, но с некоторыми заметными отличиями.

    Посредник, способствует использованию одного общего предмета, который устанавливает связь с несколькими объектами. Все объекты взаимодействуют друг с другом через посредника.

    В мире разработки программного обеспечения, шаблон посредник используется, когда система становится слишком сложной. Размещая посредников, связь может осуществляться через единый объект, вместо нескольких объектов, которые общаются друг с другом. В этом смысле, посредник может быть использован для замены системы, которая реализует шаблоны наблюдателя.
    Давайте поговорим о том, как можно его использовать. Представьте себе, что у вас есть веб-приложение, которое позволяет пользователям нажать на альбом, и играть музыку из него. Вы можете создать посредника вроде этого:

    $("#album").on("click", function(e) { e.preventDefault(); var albumId = $(this).id(); mediator.publish("playAlbum", albumId); }); var playAlbum = function(id) { … mediator.publish("albumStartedPlaying", {songList: [..], currentSong: "Without You"}); }; var logAlbumPlayed = function(id) { //Логин альбом на бенэнде }; var updateUserInterface = function(album) { //Апдейт UI для отображения того, что играет }; //Посредник subscriptions mediator.subscribe("playAlbum", playAlbum); mediator.subscribe("playAlbum", logAlbumPlayed); mediator.subscribe("albumStartedPlaying", updateUserInterface);

    Преимущество этого шаблона является в том, что один объект отвечает за связь, в то время как в шаблоне наблюдатель, несколько объектов могут прослушиваться и подписываться на друг друга.
    В паттерне наблюдатель, нет ни одного объекта, который заключает в себе ограничения. Вместо этого, наблюдатель и субъект должны сотрудничать для поддержания ограничения. Коммуникационные шаблоны определяются тем, какими путями наблюдатели и субъекты взаимосвязаны: у одного субъекта обычно много наблюдателей, а иногда наблюдатель одного субъекта является предметом другого наблюдателя.

    Заключение

    Самое замечательное в шаблонах проектирования то, что кто-то уже успешно применил их в прошлом. Существует много open-source кода, который реализует различные шаблоны в JavaScript. Как разработчики, мы должны быть в курсе, какие паттерны есть, и когда их нужно применять.

    Перевод ()
    Источник фото - Fotolia.ru

    Каждый разработчик стремится писать удобный и легко читаемый код. Как только приложения становятся больше, структурирование кода становится важной частью.
    Шаблоны проектирования играют важную роль в решении этой задачи, обеспечивая организационную структуру распространенных вопросов в конкретных обстоятельствах.
    При создании приложений веб-разработчики JavaScript часто взаимодействуют с шаблонами проектирования неосознанно.

    Как правило, они используют некоторые шаблоны проектирования больше, чем другие, хотя есть обширный перечень шаблонов проектирования на разные случаи.

    В этом посте я хотел бы обсудить распространённые шаблоны чтобы улучшить ваши знания в программировании и погрузиться глубже в JavaScript.

    Шаблоны проектирования включают в себя следующее:

    — Модуль

    — Прототип

    — Наблюдатель

    — Одиночка

    Каждый шаблон состоит из множества свойств, но я выделяю следующие ключевые моменты:

    1.Контекст: Где/при каких обстоятельствах используется тот или иной шаблон?

    2. Проблема: Какую проблему мы пытаемся решить?

    3. Решение: Как использовать это шаблон для решения этой проблемы?

    4.Реализация: Как выглядит реализация?

    # Шаблон Модуль (Module )

    В JavaScript модули являются наиболее распространенными шаблонами проектирования для обеспечения независимости каких-то частей кода от других компонентов. Это обеспечивает слабую связь для поддержания хорошо структурированного кода.
    Для тех, кто знаком с объектно-ориентированными языками, модули - это «классы» в JavaScript . Одно из многих преимуществ классов - инкапсуляция – защита состояния и поведения от доступа из других классов.
    Шаблон модуля дает доступ публичным и частным уровням (плюс менее защищенным и привилегированным).

    Этот язык UML описывает интерфейс прототипа используется для клонирования конкретных реализаций.

    Для клонирования объекта должен существовать конструктор для того чтобы создать экземпляр первого объекта. Далее, с помощью ключевого слова prototype переменные и методы привязываются к структуре объекта. Давайте рассмотрим простой пример:

    var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype.go = function() { // Вращаются колеса } TeslaModelS.prototype.stop = function() { }

    This . numWheels = 4 ;

    This . make = "Model S" ;

    TeslaModelS . prototype . go = function () {

    // Вращаются колеса

    TeslaModelS . prototype . stop = function () {

    // Применяются тормозные колодки

    Конструктор позволяет создавать один объект TeslaModelS . При создании нового объекта TeslaModelS , он сохранит состояние, инициализированное в конструкторе. Кроме того, поддержание функции go и stop несложно, так как мы объявили их при помощи прототипов. Такой же способ расширения функции с использованием прототипа описан ниже:

    var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype = { go: function() { // Вращаются колеса }, stop: function() { // Применяются тормозные колодки } }

    var TeslaModelS = function () {

    This . numWheels = 4 ;

    This . manufacturer = "Tesla" ;

    This . make = "Model S" ;

    TeslaModelS . prototype = {

    Go : function () {

    // Вращаются колеса

    Stop : function () {

    // Применяются тормозные колодки

    REVEALING PROTOTYPE PATTERN

    Так же как и шаблон модуль шаблон прототип имеет вариацию Revealing . Revealing паттерн обеспечивает инкапсуляцию с публичными и приватными членами.

    Поскольку мы возвращаем объект, мы добавим объекту-прототипу префикс функции. Дополнив наш пример, мы можем выбрать что мы хотим показать в текущем прототипе, чтобы сохранить свои уровни доступа:

    var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype = function() { var go = function() { // Вращаются колеса }; var stop = function() { // Применяются тормозные колодки }; return { pressBrakePedal: stop, pressGasPedal: go } }();

    var TeslaModelS = function () {

    This . numWheels = 4 ;

    This . manufacturer = "Tesla" ;

    This . make = "Model S" ;

    TeslaModelS . prototype = function () {

    Var go = function () {

    // Вращаются колеса

    } ;

    Var stop = function () {

    // Применяются тормозные колодки

    } ;

    Return {

    PressBrakePedal : stop ,

    PressGasPedal : go

    } () ;

    Обратите внимание, как функции Stop и Go будут защищены от возвращенного объекта в связи с нахождением за пределами области видимости возвращаемого объекта. Поскольку JavaScript изначально поддерживает прототипное наследование, нет необходимости переписывать базовые элементы(или особенности или черты).

    # Шаблон Наблюдатель (Observer )

    Бывает так, что одна часть приложения изменяется, а другие части нуждаются в обновлении. В Angular j s, если $scope объекта обновляется, событие может быть запущено для уведомления другого компонента. Шаблон Observer включает в себя то, что, если объект изменен, то он передает (broadcasts) зависимым объектам, что изменение произошло.

    Другой яркий пример архитектура модель-представление-контроллер (MVC); представление обновляется когда изменяется модель. Одним из преимуществ является разрыв связи представления от модели для уменьшения зависимостей.

    Как показано на схеме UML, необходимые объекты это subject, observer , и concrete . Объект subject содержит ссылки на concrete observers для уведомления любых изменениях. Объект observer является абстрактным классом, позволяющий concrete observers реализовывать метод уведомления.

    Давайте взглянем на пример AngularJS , который включает в себя шаблон Observer через управление событиями.

    // Controller 1 $scope.$on("nameChanged", function(event, args) { $scope.name = args.name; }); ... // Controller 2 $scope.userNameChanged = function(name) { $scope.$emit("nameChanged", {name: name}); };

    // Controller 1

    $ scope . $ on ("nameChanged" , function (event , args ) {

    $ scope . name = args . name ;

    } ) ;

    . . .

    // Controller 2

    $ scope . userNameChanged = function (name ) {

    $ scope . $ emit ("nameChanged" , { name : name } ) ;

    С шаблоном Observer важно различать независимый это объект или subject .

    Важно отметить, что, хотя шаблон Observer и предоставляет много преимуществ, но одним из недостатков является значительное падение производительности, так как количество «наблюдателей» (observers ) увеличено. Один из самых пользующихся дурной славой наблюдателей являются watchers . В AngularJS мы можем наблюдать (watch ) переменные, функции и объекты. Цикл $$digest работает и уведомляет каждого из watchers новыми значениями всякий раз, когда область объекта изменяется.

    Мы можем создать наши собственные Subjects и Observers в JavaScript . Давайте посмотрим, как это реализуется:

    var Subject = function() { this.observers = ; return { subscribeObserver: function(observer) { this.observers.push(observer); }, unsubscribeObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers.splice(index, 1); } }, notifyObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers.notify(index); } }, notifyAllObservers: function() { for(var i = 0; i < this.observers.length; i++){ this.observers[i].notify(i); }; } }; }; var Observer = function() { return { notify: function(index) { console.log("Observer " + index + " is notified!"); } } } var subject = new Subject(); var observer1 = new Observer(); var observer2 = new Observer(); var observer3 = new Observer(); var observer4 = new Observer(); subject.subscribeObserver(observer1); subject.subscribeObserver(observer2); subject.subscribeObserver(observer3); subject.subscribeObserver(observer4); subject.notifyObserver(observer2); // Observer 2 is notified! subject.notifyAllObservers(); // Observer 1 is notified! // Observer 2 is notified! // Observer 3 is notified! // Observer 4 is notified!

    var Subject = function () {

    This . observers = ;

    Return {

    SubscribeObserver : function (observer ) {

    This . observers . push (observer ) ;

    } ,

    UnsubscribeObserver : function (observer ) {

    If (index & gt ; - 1 ) {

    This . observers . splice (index , 1 ) ;

    } ,

    NotifyObserver : function (observer ) {

    Var index = this . observers . indexOf (observer ) ;

    If (index & gt ; - 1 ) {

    This . observers [ index ] . notify (index ) ;

    } ,

    NotifyAllObservers : function () {

    For (var i = 0 ; i & lt ; this . observers . length ; i ++ ) {

    This . observers [ i ] . notify (i ) ;

    } ;

    } ;

    var Observer = function () {

    Return {

    Notify : function (index ) {

    Console . log ("Observer " + index + " is notified!" ) ;

    var subject = new Subject () ;

    var observer1 = new Observer () ;

    var observer2 = new Observer () ;

    • Перевод

    Примечание переводчика: Тема наследования в JavaScript является одной из самых тяжелых для новичков. С добавлением нового синтаксиса с ключевым словом class, понимание наследования явно не стало проще, хотя кардинально нового ничего не появилось. В данной статье не затрагиваются нюансы реализации прототипного наследования в JavaScript, поэтому если у читателя возникли вопросы, то рекомендую прочитать следующие статьи: Основы и заблуждения насчет JavaScript и Понимание ООП в JavaScript [Часть 1]

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

    JavaScript является очень мощным языком. Настолько мощным, что в нем сосуществует множество различных способов проектирования и создания объектов. У каждого способа есть свои плюсы и минусы и я бы хотел помочь новичкам разобраться в этом. Это продолжение моего предыдущего поста, Хватит «классифицировать» JavaScript . Я получил много вопросов и комментариев с просьбами привести примеры, и для именно этой цели я решил написать эту статью.

    JavaScript использует прототипное наследование Это означает, что в JavaScript объекты наследуются от других объектов. Простые объекты в JavaScript, созданные с использованием {} фигурных скобок, имеют только один прототип: Object.prototype . Object.prototype , в свою очередь тоже объект, и все свойства и методы Object.prototype доступны для всех объектов.

    Массивы, созданные с помощью квадратных скобок, имеют несколько прототипов, в том числе Object.prototype и Array.prototype . Это означает, что все свойства и методы Object.prototype и Array.prototype доступны для всех массивов. Одноименные свойства и методы, например .valueOf и .ToString , вызываются из ближайшего прототипа, в этом случае из Array.prototype .

    Определения прототипа и создание объектовСпособ 1: Шаблон конструктор JavaScript имеет особый тип функции называемых конструкторами, которые действуют так же, как и конструкторы в других языках. Функции-конструкторы вызываются только с помощью ключевого слова new и связывают создаваемый объект с контекстом функции-конструктора через ключевое слово this . Типичный конструктор может выглядеть следующим образом:
    function Animal(type){ this.type = type; } Animal.isAnimal = function(obj, type){ if(!Animal.prototype.isPrototypeOf(obj)){ return false; } return type ? obj.type === type: true; }; function Dog(name, breed){ Animal.call(this, "dog"); this.name = name; this.breed = breed; } Object.setPrototypeOf(Dog.prototype, Animal.prototype); Dog.prototype.bark = function(){ console.log("ruff, ruff"); }; Dog.prototype.print = function(){ console.log("The dog " + this.name + " is a " + this.breed); }; Dog.isDog = function(obj){ return Animal.isAnimal(obj, "dog"); };
    Использование этого конструктора выглядит также как и создание объекта в других языках:
    var sparkie = new Dog("Sparkie", "Border Collie"); sparkie.name; // "Sparkie" sparkie.breed; // "Border Collie" sparkie.bark(); // console: "ruff, ruff" sparkie.print(); // console: "The dog Sparkie is a Border Collie" Dog.isDog(sparkie); // true
    bark и print методы прототипа, которые применяются для всех объектов созданных с помощью конструктора Dog . Свойства name и breed инициализируются в конструкторе. Это общепринятая практика, когда все методы определяются в прототипе, а свойства инициализируются конструктором.Способ 2: Определение класса в ES2015 (ES6) Ключевое слово class было зарезервировано в JavaScript с самого начала и вот наконец-то пришло время его использовать. Определения классов в JavaScript схоже с другими языками.
    class Animal { constructor(type){ this.type = type; } static isAnimal(obj, type){ if(!Animal.prototype.isPrototypeOf(obj)){ return false; } return type ? obj.type === type: true; } } class Dog extends Animal { constructor(name, breed){ super("dog"); this.name = name; this.breed = breed; } bark(){ console.log("ruff, ruff"); } print(){ console.log("The dog " + this.name + " is a " + this.breed); } static isDog(obj){ return Animal.isAnimal(obj, "dog"); } }
    Многие люди считают этот синтаксис удобным, потому что он объединяет в одном блоке конструктор и объявление статичных и прототипных методов. Использование точно такое же, как и в предыдущем способе.
    var sparkie = new Dog("Sparkie", "Border Collie"); Способ 3: Явное объявление прототипа, Object.create, фабричный метод Этот способ показывает, что на самом деле новый синтаксис с ключевым словом class использует прототипное наследование. Также этот способ позволяет создать новый объект без использования оператора new .
    var Animal = { create(type){ var animal = Object.create(Animal.prototype); animal.type = type; return animal; }, isAnimal(obj, type){ if(!Animal.prototype.isPrototypeOf(obj)){ return false; } return type ? obj.type === type: true; }, prototype: {} }; var Dog = { create(name, breed){ var proto = Object.assign(Animal.create("dog"), Dog.prototype); var dog = Object.create(proto); dog.name = name; dog.breed = breed; return dog; }, isDog(obj){ return Animal.isAnimal(obj, "dog"); }, prototype: { bark(){ console.log("ruff, ruff"); }, print(){ console.log("The dog " + this.name + " is a " + this.breed); } } };
    Этот синтаксис удобен, потому что прототип объявляется явно. Понятно что определено в прототипе, а что определено в самом объекте. Метод Object.create удобен, потому что он позволяет создать объект от указанного прототипа. Проверка с помощью .isPrototypeOf по-прежнему работает в обоих случаях. Использование разнообразно, но не чрезмерно:
    var sparkie = Dog.create("Sparkie", "Border Collie"); sparkie.name; // "Sparkie" sparkie.breed; // "Border Collie" sparkie.bark(); // console: "ruff, ruff" sparkie.print(); // console: "The dog Sparkie is a Border Collie" Dog.isDog(sparkie); // true Способ 4: Object.create, фабрика верхнего уровня, отложенный прототип Этот способ является небольшим изменение способа 3, где сам класс является фабрикой, в отличии от случая когда класс является объектом с фабричным методом. Похоже, на пример конструктора (способ 1), но использует фабричный метод и Object.create .
    function Animal(type){ var animal = Object.create(Animal.prototype); animal.type = type; return animal; } Animal.isAnimal = function(obj, type){ if(!Animal.prototype.isPrototypeOf(obj)){ return false; } return type ? obj.type === type: true; }; Animal.prototype = {}; function Dog(name, breed){ var proto = Object.assign(Animal("dog"), Dog.prototype); var dog = Object.create(proto); dog.name = name; dog.breed = breed; return dog; } Dog.isDog = function(obj){ return Animal.isAnimal(obj, "dog"); }; Dog.prototype = { bark(){ console.log("ruff, ruff"); }, print(){ console.log("The dog " + this.name + " is a " + this.breed); } };
    Этот способ интересен тем, что похож на первой способ, но не требует ключевого слова new и работает с оператором instanceOf . Использование такое же, как и в первом способе, но без использования ключевого слова new :
    var sparkie = Dog("Sparkie", "Border Collie"); sparkie.name; // "Sparkie" sparkie.breed; // "Border Collie" sparkie.bark(); // console: "ruff, ruff" sparkie.print(); // console: "The dog Sparkie is a Border Collie" Dog.isDog(sparkie); // true СравнениеСпособ 1 против Способа 4 Существует довольно мало причин, для того чтобы использовать Способ 1 вместо Способа 4. Способ 1 требует либо использование ключевого слова new , либо добавление следующей проверки в конструкторе:
    if(!(this instanceof Foo)){ return new Foo(a, b, c); }
    В этом случае проще использовать Object.create с фабричным методом. Вы также не можете использовать функции Function#call или Function#apply с функциями-конструкторами, потому что они переопределяют контекст ключевого слова this . Проверка выше, может решить и эту проблему, но если вам нужно работать с неизвестным заранее количеством аргументов, вы должны использовать фабричный метод.Способ 2 против Способа 3 Те же рассуждения о конструкторах и операторе new , что были упомянуты выше, применимы и в этом случае. Проверка с помощью instanceof необходима, если используется новый синтаксис class без использования оператора new или используются Function#call или Function#apply .Мое мнение Программист должен стремиться к ясности своего кода. Синтаксис Способа 3 очень четко показывает, что именно происходит на самом деле. Он также позволяет легко использовать множественное наследование и стековое наследования. Так как оператор new нарушает принцип открытости/закрытости из-за несовместимости с apply или call , его следует избегать. Ключевое слово class скрывает прототипный характер наследования в JavaScript за маской системы классов.
    «Простое лучше мудреного», и использование классов, потому что оно считается более «изощренным» является просто ненужной, технической головомойкой.
    Использование Object.create является более выразительным и ясным, чем использование связки new и this . Кроме того, прототип хранится в объекте, который может быть вне контекста самой фабрики, и таким образом может быть более легко изменен и расширен добавлением методов . Прям как классы в ES6.
    Ключевое слово class , возможно будет наиболее пагубной чертой в JavaScript. Я испытываю огромное уважение к блестящим и очень трудолюбивым людям, которые были вовлечены в процесс написания стандарта, но даже блестящие люди иногда делают неправильные вещи. - Eric Elliott
    Добавление чего-то ненужного и возможно пагубного, противоречащего самой природе языка является необдуманным и ошибочным.
    Если вы решите использовать class , я искренне надеюсь, что мне никогда не придется работать с вашим кодом. На мой взгляд, разработчики должны избегать использования конструкторов, class и new , и использовать методы, которые более естественны парадигме и архитектуре языка. ГлоссарийObject.assign(a, b) копирует все перечислимые (enumerable) свойства объекта b в объект a , а затем возвращает объект a
    Object.create(proto) создает новый объект от указанного прототипа proto
    Object.setPrototypeOf(obj, proto) меняет внутреннее свойство [] объекта obj на proto

    Теги: Добавить метки

    Привет, хабр!
    С удивлением обнаружил отсутствие на хабре развернутой статьи о сабже, что немедленно сподвигло меня исправить эту вопиющую несправедливость.

    В условиях когда клиентская часть веб-приложений становится все более толстой, бизнес-логика неумолимо переползает на клиент, а на суверенитет серверных технологий все более смело посягает node.js нельзя не задуматься о приемах проектирования архитектуры на javaScript. И в этом деле нам несомненно должны помочь паттерны проектирования - шаблонные приемы решения часто встречающихся задач. Паттерны помогают построить архитектуру, которая потребует от вас наименьших усилий при необходимости внести изменения. Но не стоит воспринимать их как панацею, т.е., грубо говоря, если качество кода «не фонтан», он кишит хардкодом и жесткой связью между логически независимыми модулями, то никакие паттерны его не спасут. Но если стоит задача спроектировать масштабируемую архитектуру, то паттерны могут стать хорошим подспорьем.
    Но впрочем эта статья не о паттернах проектирования как таковых, а о их применении в javaScript. В первой части этой статьи я напишу о применении порождающих паттернах.

    Singleton Если бы стояла задача описать этот паттерн одной фразой, то она получилась бы примерно следующей: Singleton - это класс, который может иметь только один экземпляр.
    Самым простым и очевидным решением в javaScript для реализации этого паттерна является использование объектов:

    Var app = { property1: "value", property2: "value", ... method1: function () { ... }, ... }

    Этот способ имеет как свои преимущества, так и недостатки. Его просто описать, многие его используют не догадываясь о существовании каких-либо паттернов и эта форма записи будет понятна любому javaScript разработчику. Но у него есть и существенный недостаток: основная цель паттерна singleton - обеспечить доступ к объекту без использования глобальных переменных, а данный способ предоставляет доступ к переменной app только в текущей области видимости. Это означает, что к объекту app мы сможем обратиться из любого места приложения только в том случае если он будет глобальным. Чаще всего это крайне неприемлемо, хорошим стилем разработки на javaScript является использование максимум одной глобальной переменной, в которой инкапсулируется все необходимое. А это означает, что приведенный выше подход мы сможем использовать максимум один раз в приложении.
    Второй способ чуть более сложен, но зато и более универсален:

    Function SomeFunction () { if (typeof (SomeFunction.instance) == "object") { return SomeFunction.instance; } this.property1 = "value"; this.property2 = "value"; SomeFunction.instance = this; return this; } SomeFunction.prototype.method1 = function () { }

    Теперь, используя любую модульную систему (например requirejs) мы в любом месте нашего приложения сможем подключить файл с описанием этой функции-конструктора и получим доступ к нашему объекту, выполнив:

    Var someObj = new SomeFunction ();

    Но этот способ также имеет свой недостаток: экземпляр хранится просто как статическое свойство конструктора, что позволяет кому угодно его перезаписывать. Мы же хотим чтобы при любых обстоятельствах мы могли получить доступ из любого уголка нашего приложения к требуемому объекту. Это означает, что переменную, в которой мы сохраним экземпляр надлежит сделать приватной, а поможет нам в этом замыкания.

    Function SomeFunction () { var instance; SomeFunction = function () { return instance; } this.property1 = "value"; this.property2 = "value"; instance = this; }

    Казалось бы вот оно, решение всех проблем, но на место старых проблем приходят новые. А именно: все свойства, занесенные в прототип конструктора после создания экземпляра не будут доступны, т.к. по сути будут записаны в старый конструктор, а не в свежеопределенный. Но и из этой ситуации есть достойный выход:

    Function SomeFunction () { var instance; SomeFunction = function () { return instance; } SomeFunction.prototype = this; instance = new SomeFunction (); instance.constructor = SomeFunction; instance.property1 = "value"; instance.property2 = "value"; return instance; }

    Этот способ описания одиночки лишен всех вышеперечисленных недостатков и вполне пригоден для универсального использования, однако, способы описания одиночки с помощью замыкания не будут работать с requirejs, но если немного их модифицировать и вынести переменную из замыкания, созданного самой функцией в функцию, используемую в define, то проблема будет решена:

    Define(, function () { var instance = null; function SomeFunction() { if (instance) { return instance; } this.property1 = "value"; this.property2 = "value"; instance = this; }; return SomeFunction; });

    Factory method У фабричного метода две основных цели:
    1) Не использовать явно конкретные классы
    2) Объединить вместе часто используемые методы инициализации объектов
    Простейшей реализацией фабричного метода является такой пример:

    Function Foo () { //... } function Bar () { //... } function factory (type) { switch (type) { case "foo": return new Foo(); case "bar": return new Bar(); } }

    Соответственно создание объектов будет выглядеть так:

    Foo = factory("foo"); bar = factory("bar");

    Можно использовать более элегантное решение:

    Function PetFactory() { }; PetFactory.register = function(name, PetConstructor) { if (name instanceof Function) { PetConstructor = name; name = null; } if (!(PetConstructor instanceof Function)) { throw { name: "Error", message: "PetConstructor is not function" } } this = PetConstructor; }; PetFactory.create = function(petName) { var PetConstructor = this; if (!(PetConstructor instanceof Function)) { throw { name: "Error", message: "constructor "" + petName + "" undefined" } } return new PetConstructor(); };

    В этом случае мы не ограничиваем себя количеством классов, которые может порождать фабрика, можем добавлять их сколько угодно таким способом:

    PetFactory.register("dog", function() { this.say = function () { console.log("gav"); } });

    Ну или таким:

    Function Cat() { } Cat.prototype.say = function () { console.log("meow"); } PetFactory.register(Cat);

    Abstract Factory Абстрактная фабрика применяется для создания группы взаимосвязанных или взаимозависимых объектов.
    Предположим у нас есть несколько всплывающих окон, которые состоят из одинаковых элементов, но элементы эти по-разному выглядят и по-разному реагируют на действия пользователя. Каждый из этих элементов будет создаваться фабричным методом, а это значит, что для каждого вида всплывающих окон нужна своя фабрика объектов.
    Для примера опишем фабрику BluePopupFactory, она имеет точно такую же структуру как PetFactory, поэтому опустим подробности и просто будем ее использовать.

    Function BluePopup () { //создание всплывающего окна } BluePopup.prototype.attach = function (elemens) { //присоединение других ui-элементов к окну } BluePopupFactory.register("popup", BluePopup); function BluePopupButton () { //создание кнопки для синего всплывающего окна } BluePopupButton.prototype.setText = function (text) { //установка текста на кнопке } BluePopupFactory.register("button", BluePopupButton); function BluePopupTitle () { //создание заголовка для синего окна } BluePopupTitle.prototype.setText = function (text) { //установка текста заголовка } BluePopupFactory.register("title", BluePopupTitle);

    Наверное у нас должен быть некий класс, отвечающий за элементы интерфейса.

    Function UI () { //класс, отвечающий за ui-элементы }

    И в него мы добавим метод createPopup:

    UI.createPopup = function (factory) { var popup = factory.create("popup"), buttonOk = factory.create("button"), buttonCancel = factory.create("button"), title = factory.create("title"); buttonOk.setText("OK"); buttonCancel.setText("Cancel"); title.setText("Untitled"); popup.attach(); }

    Как видите createPopup принимает аргументом фабрику, создает само всплывающее окно и кнопки с заголовком для него, а затем присоединяет их к окну.
    После этого можно использовать этот метод так:

    Var newPopup = UI.createPopup(BluePopupFactory);

    Соответственно, можно описать неограниченное количество фабрик и передавать нужную при создании очередного всплывающего окна.

    В WordPress повсюду используются шаблоны и Javascript там не исключение. В этой заметке поговорим про встроенную в WordPress возможность создавать HTML шаблоны, которые затем можно использовать в JS. Создаются и используются такие шаблоны очень просто, впрочем как и многое другое в WordPress.

    Есть много способов создавать шаблоны в Javascript, для них даже придумана отдельная спецификация именуемая Mustache . Она реализована на многих языках, включая Javascript. Например, библиотека Handlebars использует эту спецификацию и даже немного её расширяет. Или популярная мини-библиотека Underscore .

    С версии 3.5 WordPress уже имеет в своем ядре удобный шаблонизатор для JS. Он например используется в админке при создании блоков для медиа-загрузчика. В основе лежит вышеупомянутая библиотека Underscore , синтаксис немного переделан, чтобы больше соответствовать спецификации Mustache .

    Для создания шаблонов в WordPress есть метод wp.template

    wp.template(id)

    Создает объект шаблона из HTML кода. Чтобы получить готовый HTML код для использования в JS, в созданный объект нужно передать данные для заполнения шаблона.

    Возвращает

    Function. Функцию, в которую нужно передать данные для интерполяции шаблона.

    Использование var template = wp.template(id); var HTML = template(data); id(строка)

    Идентификатор HTML элемента который содержит HTML код шаблона. HTML элемент должен иметь указанный тут атрибут id с префиксом tmpl- .

    Например, если тут указать foo , то HTML элемент должен иметь id id="tmpl-foo" .

    Data(объект) JS объект данных, которые будут использованы для заполнения шаблона. Например: { text: "Привет" } .

    Заполнение шаблона (интерполяция)
    • {{{data.unescaped}}} - неочищенные данные.
    • {{data.escaped}} - очищенные данные.
    • - обработать js (eval).
    Префикс data.

    data в шаблоне - это объект исходных данных. В шаблоне нужно использовать именно ключ data .

    Чтобы соответствовать структуре данных возвращаемых функциями: wp_send_json_success() и wp_send_json_error() , wp.template оборачивает все полученные данные в переменную data . Поэтому перед каждым параметром в шаблоне нужно указывать data. , иначе мы получим ошибку: {property} is not defined .

    Правильно {{{data.name}}}

    Неправильно {{{name}}}

    Пример шаблона

    Это будет просто выведено.

    Выведем значение переменной escapedValue {{data.escapedValue}}.

    Если данные содержат разметку, выводим без экранирования:

    {{{data.unescapedValue}}}

    Когда нужно выполнить какую-то логику.

    Будет выведено, только если data.trueValue = true.

    Создание и генерация шаблона Создание шаблона

    Чтобы шаблон никак не фигурировал в DOM дереве, его принято создавать в теге script с указанием типа type="text/html" .

    Привет {{{data.name}}}

    Атрибут id должен начинаться с tmpl- , все что после этого префикса будет затем использовано в функции wp.template("my-template") .

    Создание шаблона в теге script это хак, который отлично подходит для создания html элемента, который никак не используется браузером. Когда указан непонятный для браузера тип он просто игнорирует html тег, а это нам и нужно.

    Шаблон также можно создать в любом другом HTML элементе (например в , который затем можно скрыть), единственное что нужно это указать id атрибут.

    Также для создания шаблонов есть специальный HTML тег , однако он не поддерживается в IE . Но в целом он довольно актуален.

    Генерация шаблона

    wp.template() возвращает функцию, поэтому не пытайтесь передать результат в html элемент или вывести результат в консоль. Обычно результат wp.template() передается в переменную, а затем эта переменная используется как функция и в неё передаются данные, которыми должен быть заполнен шаблон.

    Пример (шаблон указан выше)

    // JS var template = wp.template("my-template"), data = { name: "Виктор" }; jQuery(".my-element").html(template(data));

    В результате получим в HTML:

    Привет Виктор

    Пример комментирования на AJAX с использование шаблона

    Создаем шаблон и подключаем скрипт в файле темы functions.php:

  • {{{data.gravatar}}} {{data.comment_author}}



  • Похожие публикации