С некоторых пор я озаботился поиском интересных технологий веб-разработки, в частности довольно плотно занялся изучением специфических решений на JavaScript + HTML + Java/Flash(ActionScript). Именно так я, кстати, и забрёл на хабрахабр.
Я нашёл много интересного, но обо всём по порядку. Я обнаружил, что практически каждая уважающая себя библиотека в той или иной форме пытается реализовать некое подобие классового подхода (или «классовой парадигмы», если так понятнее) на JS — это и MooTools, и qooxdoo, и Prototype (хотя это и мало соотносится с его названием) и многие другие. Я мог бы поспорить с тем, что ООП в «классическом» (т.е. классовом) его понимании так уж необходим в JS — сильно развитые возможности функционального программирования в этом языке могут быть гораздо эффективнее — но раз классический подход реализуется, значит он востребован, что, в общем-то, не особо удивляет, т.к. ну привыкли мы уже так писать, и не особо хочется переучиваться.
Меж тем, как я заметил, присутствует и ещё один неприятный аспект — реализация некоего «классического ООП JavaScript», под которым понимается обычно связка прототипизации и замыканий. Такой подход выглядит не менее однобоко, чем реализация классовой парадигмы, однако воспринимается более «родственно» языку. Такой подход имеет один очень существенный недостаток — при реализации секции private функции плодятся как кролики, и память становится их капустой. При том, что в браузере пользователя обычно открыто штук по десять вкладок с сайтами, каждый из которых делает нечто похожее, производительность от этого здорово падает.
О том, как надо бы использовать JS, можно прочитать хотя бы
здесь. Но я уверен, что люди по прежнему будут пытаться сделать JS объектно-ориентированным. Поэтому задумаемся над тем, как дать таким людям возможность использовать язык привычным «объектным» способом, покуда они не начали постигать его «функциональную» суть.
У всех реализаций есть один существенный недостаток — их private и protected (если таковые имеются) поля и методы являются обычным фэйком, и, как правило, не составляет никаких проблем просто подменить arguments.callee._class на ClassWidthPrivateMethod — и вуа-ля, мы вызвали приватный метод извне. Язык, который позволяет что-то реализовать, с тем же успехом позволяет всё это обойти. Не менее неприятным моментом любой реализации является то, что заявления типа «при классическом подходе создаётся дикое количество экземпляров функций» полностью разбиваются о тот факт, что сама реализация создаёт внутри своих систем защиты не меньшее, а то и большее количество экземпляров одной и той же функции, при том, как правило, не самой маленькой. Так, спрашивается, зачем всё это нужно.
Только спортивного интереса ради и изучения языка для я реализовал библиотеку, которая позволит использовать ООП-подход с хоть сколь-нибудь заметной пользой. Вы можете скачать два файла с нашего сайта:
class.stanalone.js
run.init.js
Первый файл — это библиотека, предназначенная для standalone использования (в оригинальной реализации она является частью нашей платформы JACK), а второй — скрипт для тестирования.
Библиотека пока ещё достаточно сыра, да и её системы защиты, наверняка, можно обойти (кстати, если кто-то это сделает, то сообщите на akaj@evounit.ru, что бы я мог исправить ошибки). Но изучив её исходный код вы, возможно, что-то подчерпнёте для себя. Или, может быть, она просто вам пригодится в работе.
Интерфейс библиотека выглядит так:
Class() — функция для объявления нового класса.
Class.GUID() — функция возвращает уникальный идентификатор класса (может пригодится зачем).
Class.Self() — функция возвращает ссылку на сам класс.
Class.Superior() — функция возвращает ссылку на родительский класс.
Class.Interface(_index) — функция возвращает ссылку на интерфейс класса с индексом _index.
Class.Interfaces() — функция возвращает массив интерфейсов класса.
Class.ConstructorFunction() — функция вызывает конструктор применимо к своему контексту this.
Class.Create(...) — функция создаёт экземпляр класса и вызывает его конструктор с переданными параметрами.
Class.HeritableNames() — функция возвращает список членов класса.
Class.Extends(_superior) — функция указывает родительский класс.
Class.Implements(_interfaces|_interface1, _interface2, ...) — функция указывает интерфейсы класса. Аргументы можно передавать напрямую или в виде массива.
Class.OffspringOf(_ancestor) — функция проверяет, является ли класс потомком класса _ancestor.
Class.Constructor(_function) — функция указывает конструктор класса.
Class.Define(_list) — функция указывает члены прототипа класса. _list должен быть объектом, поля которого будут использованы для присвоения.
Class.Var(_access, _list) — функция указывает переменные класса. Доступ к переменной можно полчить в виде ClassInstance.varName() для считывания и ClassInstance.varName(varValue) для записи значения. _access должен быть строкой, которая может содержать следующие лексеммы:
private (закрытые члены класса),
protected (защищённые),
public (открытые),
static (статические члены или члены класса) и
readonly (член доступен только для чтения).
Class.Method(_access, _list) — функция указывает методы класса. Объявлять метод readonly не имеет смысла.
Class.HasMethod(_function) — функция проверяет, есть ли у класса указанный метод.
Class.Create создаёт ClassInstance, которые обладает следующим интерфейсом:
ClassInstance.GUID() — функция возвращает уникальные идентификатор объекта.
ClassInstance.Self() — функция возвращает класс объекта.
ClassInstance.Superior() — функция возвращает ClassWrapper (см. ниже) для родительского класса.
ClassInstance.Interface(_index) — функция возвращает ClassWrapper для интерфейса с индексом _index.
ClassInstance.Interfaces() — функция возвращает массив ClassWrapper'ов для интерфейсов.
ClassInstance.Call(_name, ...) — функция осуществляет вызов метода по его имени.
ClassInstance.Wrap(_class) — функция создаёт ClassWrapper для класса _class.
ClassWrapper — этот оболочка для ClassInstance, представляющая её экземпляром указанного класса. Интерфейс оболочки выглядит следующим образом:
ClassWrapper.Self() — функция возвращает ссылку на класс оболчки.
ClassWrapper.Superior() — функция возвращает ClassWrapper для родительского класса класса оболочки.
ClassWrapper.Interface(_index) — функция возвращает ClassWrapper для интерфейса оболочки с индексом _index.
ClassWrapper.Interfaces() — функция возвращает массив ClassWrapper'ов для интерфейсов оболочки.
ClassWrapper.Construct(...) — функция вызывает конструктор класса оболочки применимо к ClassInstance.
ClassWrapper.Call(_method, ...) — функция вызывает метод прототипа класса оболочки применимо к ClassInstance.
Библиотека будет расширяться и дорабатываться. Я по прежнему рекомендую использовать возможность функционального программирования в JS, но библиотека — в помощь. По все вопросам вы можете писать на akaj@evounit.ru.
Спасибо за ваше внимание,
Александр Кай.