...

воскресенье, 16 июня 2013 г.

Прототипно-ориентированный 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 спроектирован, как чисто прототипно-ориентированный язык, поэтому традиционная классовая модель для него избыточна и чужда.

Что думаете…


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий