Search
Write a publication
Pull to refresh

Размышления об ООП в Javascript.

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

Спасибо за ваше внимание,
Александр Кай.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.