Прототипно-ориентированный JavaScript и совсем немножко кодинга

от автора

Моя попытка проникнуться прототипами в JavaScript. Что из этого получилось, и стоит ли развиваться в данном направлении?

Статья состоит из объяснения прототипов в JavaScript на примерах, затем я рассказываю о своей попытке углубиться и сделать набросок относительно сложного VC каркаса, в конце я задам и предложу ответить на философский вопрос: «Чистый прототипно-ориентированный или объектно-ориентированный подход с применением классов?».

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

Что такое прототипы на примерах

В распространенной концепции объектно-ориентированного программирования классами, мы создаем новый класс при помощи: функции-конструктора, далее описываем необходимые свойства и методы, создаем новый экземпляр при помощи ключевого слова new.

Как мы пытаемся делать это в JavaSript:

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

Нам необходимо создать два класса-наследника, в которых будет переопределен метод render и определено свойство extension, то есть мы будем иметь еще два специальных класса для отображения PDF и TXT файлов.

var extend = function(obj, extObj) {     var k, newObj = {};     for (k in obj) {         newObj[k] = obj[k];     }     for (k in extObj) {        newObj[k] = extObj[k];    }    return newObj; };  var File = function(name, extension) {     this.name = name;     this.extension = extension; };  File.prototype = {     name: null,     extension: null,      render: function() {         console.log(‘Default file render: ’, this.name);     } };  PdfFile = function(name) {     this.name = name; };  PdfFile.prototype = extend(File.prototype, {     extension: ‘pdf’,     render: function() {         console.log(‘PDF file render: ’, this.name);     } });  TxtFile.prototype = extend(File.prototype, {     extension: ‘txt’,     render: function() {         console.log(‘TXT file render: ’, this.name);     } });  var txtFileInstance = new TxtFile(‘Name of TXT file’); var pdfFileInstance = new PdfFile(‘Name of PDF file’); 
А теперь взглянем как это можно сделать при помощи прототипов:

Я буду использовать объект Obj для упрощения инициализации переменных объекта, он не обязателен.

var Obj = {   create: function(values) {     var k;      values = values || {};        for (var k in values) {       values[k] = {         value: values[k],         writable: true,          configurable: true,          enumerable: true       };     }          return Object.create(this, values);   } };  var File = Obj.create({     name: null,     extension: null,          render: function() {         console.log(‘Default file render: ’, this.name);     } });  var PdfFile = File.create({     name: null,     extension: ‘pdf’,          render: function() {         console.log(‘PDF file render: ’, this.name);     } });  var TxtFile = File.create({     name: null,     extension: ‘txt’,          render: function() {         console.log(‘TXT file render: ’, this.name);     } });  var txtFileInstance = TxtFile.create({    name: ‘Name of TXT file’ });  var pdfFileInstance = PdfFile.create({    name: ‘Name of PDF file’ }); 

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

В стандарте ECMAScript 6 предлагают конструкцию для создания привычных классов. Я не могу определенно сказать: поможет это или испортит язык, но программистам с устоявшемся классовым мышлением определенно станет проще.

Что происходит в примерах, что общего, что различного?

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

Различное: в первом примере происходит копирование прототипа, что делает прототип «статическим». Любые изменения прототипа в процессе работы не будут отражены в наследниках.

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

Из субъективных наблюдений: второй пример кажется более естественным в контексте JavaScript.

Подробнее про прототипы в статье «Нужны ли в JavaScript классы?».

Набросок относительно сложного VC каркаса

После прочтенных статей и экспериментов с прототипами я решил применить на практике полученные знания.

В работе использую Senca Touch, поэтому написал каркас, имеющий схожее API.

Код приложения и живой пример доступны на JS Bin.

Структура получившегося приложения:

  • Объект X, содержащий базовые объекты
    • Базовый объект X.Object, прототип и точка создания новых объектов
    • Базовый объект X.App
    • Базовый объект X.View
    • Базовый объект X.Controller

  • Объекты приложения listView, listController, app

Приложение представляет из себя простой список UL и кнопка включающая и выключающая отображение списка.

Объект X.Object имеет метод X.Object.create для создания новых объектов-наследников. Другими словами — любой объект приложения является потомком X.Object.

Объект X.View реализует обертку для создания DOM элемента, содержащего вложенные объекты X.View. Метод X.View.render вызывается рекурсивно у всех вложенных элементов и возвращает корневой DOM элемент.

Объект X.Controller используется для добавления слушателей к элементам, содержит методы для управления доверенной контроллеру View. Содержит метод X.Controller.render, который производит рендер View и добавление слушателей, как результат возвращает DOM элемент.

Объект X.App используется для хранения контейнера в который должны добавляться результат X.Controller.render, а так же список контроллеров, используемых в приложении. Метод X.App.run обходит все контроллеры приложения, запуская X.Controller.render, результат добавляется в контейнер приложения.

Приложение выполняет достаточно простую функцию и в текущем состоянии оно не доказывает преимуществ прототипно-ориентированного подхода, но зато оно позволяет взглянуть на использование прототипов на практике.

В комментариях можно оставить ссылки на проекты, использующие хорошо организованный код с применением прототипно-ориентированного подхода. Буду признателен.

Время подвести черту и задать вопрос

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

Что думаете вы…

Чистый прототипно-ориентированный или объектно-ориентированный подход с применением классов?

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

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/183450/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *