UNPKG

ct

Version:

Это ООП библиотека для node.js, которая позволяет создавать полноценные классы на чистом JavaScript-е

436 lines (361 loc) 19.1 kB
/** * CT.js v.1.0.5 - Licensed under the MIT license * http://github.com/classtype/ct * ---------------------------------------------- * * See http://github.com/classtype/ct/graphs/contributors for the full list of contributors. * Thank you all, you're awesome! * * Примечание: * - В данном файле понятие тип(type) используется только для обозначения типов доступа(access type) * */ var CT = CT || (function() { /*-------------------------------------------------------------------------------------------------- | | -> Копирует метод или свойство | |-------------------------------------------------------------------------------------------------*/ var copy = function(parent, child, field, warp) { // Получаем родительские геттер и сеттер var getter = parent.__lookupGetter__(field); var setter = parent.__lookupSetter__(field); // Проверяем наличие геттера и сеттера if (getter || setter) { if (getter) child.__defineGetter__(field, warp ? warp(getter) : getter); if (setter) child.__defineSetter__(field, warp ? warp(setter) : setter); } // Обычный метод или свойство else child[field] = warp ? warp(parent[field]) : parent[field]; }; /*-------------------------------------------------------------------------------------------------- | | -> Проверяет наличие в объекте функции, геттера или сеттера | |-------------------------------------------------------------------------------------------------*/ var isMethod = function(obj, field) { // Функция if (typeof obj[field] == 'function') return true; // Геттер if (obj.__lookupGetter__(field)) return true; // Сеттер if (obj.__lookupSetter__(field)) return true; // В объекте obj поле field не является не функцией, не геттером и не сеттером return false; }; /*-------------------------------------------------------------------------------------------------- | | -> Преобразовает из исходного во внутренне представление (для поиска и переопределения) | |-------------------------------------------------------------------------------------------------*/ var parse = function(args) { // Методы и свойства var p = [ // Статика {},// Список типов доступа {},// Список методов и свойств // Динамика {},// Список типов доступа {}// Список методов и свойств ]; // Список типов доступа var types = { public: 0,// Публичные protected: 1,// Защищенные private: 2// Приватные }; // Проходим по списку методов и свойств for (var i = 0; i < args.length; i++) { // Тип доступа (public/protected/private) for (var type in args[i]); // Статика if (type == 'static') { // Тип доступа (public/protected/private) for (var typeStatic in args[i][type]); // Название метода for (var field in args[i][type][typeStatic]); // Сохраняем тип доступа p[0][field] = types[typeStatic]; // Копируем метод или свойство copy(args[i][type][typeStatic], p[1], field); } // Динамика else { // Название метода for (var field in args[i][type]); // Сохраняем тип доступа p[2][field] = types[type]; // Копируем метод или свойство copy(args[i][type], p[3], field); } } // Возвращаем внутренне представление (для поиска и переопределения) return p; }; /*-------------------------------------------------------------------------------------------------- | | -> Преобразовает из исходного во внутренне представление (для конечного пользования) | |-------------------------------------------------------------------------------------------------*/ var getParams = function(child) { // Список параметров var p = [ // Статика [ // Методы {},// Публичные {},// Защищенные {}// Приватные ], [ // Свойства {},// Публичные {},// Защищенные {}// Приватные ], // Динамика [ // Методы {},// Публичные {},// Защищенные {}// Приватные ], [ // Свойства {},// Публичные {},// Защищенные {}// Приватные ] ]; // Проходим по статике и динамике for (var i = 0; i <= 2; i += 2) { // Проходим по списку методов и свойств for (var field in child[1 + i]) { // Копируем метод if (isMethod(child[1 + i], field)) { copy(child[1 + i], p[i][child[i][field]], field); } // Копируем свойство else { copy(child[1 + i], p[1 + i][child[i][field]], field); } } } // Возвращаем внутренне представление (для конечного пользования) return p; }; /*-------------------------------------------------------------------------------------------------- | | -> Переопределяет методы или свойства для одного типа доступа | |-------------------------------------------------------------------------------------------------*/ var extendType = function(parent, child, warp) { // Проходим по списку свойств for (var field in parent) { // Копируем свойство copy(parent, child, field, warp); } }; /*-------------------------------------------------------------------------------------------------- | | -> Переопределяет методы или свойства для нескольких типов доступа | |-------------------------------------------------------------------------------------------------*/ var extendTypes = function(parent, child) { // Проходим по списку типов доступа for (var i = 0; i < parent.length; i++) { // Проходим по списку методов extendType(parent[i], child); } }; /*-------------------------------------------------------------------------------------------------- | | -> Добавляет список статических методов и свойств в Self и Private | |-------------------------------------------------------------------------------------------------*/ var extendStatic = function(Self, Private, params) { // Создаем объект для статических методов и свойств var _self = {}; // Добавляем свойство self // для доступа к статическим методам и свойствам Object.defineProperty(_self, 'self', { value: _self }); // Добавляем свойство self к прототипу класса Private // для доступа к статическим приватным методам и свойствам // через все прототипные методы, геттеры, сеттеры и публичные свойства Object.defineProperty(Private.prototype, 'self', { value: _self }); // Добавляем список методов к свойству self прототипу класса Private // Типы доступа: Публичные, Защищенные, Приватные extendTypes(params[0], Private.prototype.self); // Добавляем список свойств к свойству self прототипу класса Private // Типы доступа: Публичные, Защищенные, Приватные extendTypes(params[1], Private.prototype.self); // Добавляем список публичных статических методов к классу Self // Типы доступа: Публичные extendType(params[0][0], Self, function(method) { return function() { return method.apply(_self, arguments); } }); // Проходим по списку свойств for (var field in params[1][0]) { // Перевод свойства в геттер и сеттер (function(field) { Self.__defineGetter__(field, function() { return _self[field]; }); Self.__defineSetter__(field, function(val) { _self[field] = val; }); })(field); } }; /*-------------------------------------------------------------------------------------------------- | | -> Добавляет список методов и свойств к прототипам классов Self и Private | |-------------------------------------------------------------------------------------------------*/ var extendPrototype = function(Self, Private, params, privateField, pass) { // Добавляем список публичных методов к прототипу класса Self // Типы доступа: Публичные extendType(params[2][0], Self.prototype, function(method) { return function() { return method.apply(this[privateField](pass), arguments); } }); // Добавляем список публичных свойств к прототипу класса Self // Типы доступа: Публичные for (var field in params[3][0]) { // Перевод свойства в геттер и сеттер (function(field) { Self.prototype.__defineGetter__(field, function() { return this[privateField](pass)[field]; }); Self.prototype.__defineSetter__(field, function(val) { this[privateField](pass)[field] = val; }); })(field); } // Добавляем список методов к прототипу класса Private // Типы доступа: Публичные, Защищенные, Приватные extendTypes(params[2], Private.prototype); }; /*-------------------------------------------------------------------------------------------------- | | -> Переопределяет методы | |-------------------------------------------------------------------------------------------------*/ var extend = function(parent, child, type) { // Проходим по статике и динамике for (var i = 0; i <= 2; i += 2) { // Проходим по списку методов и свойств for (var field in parent[1 + i]) { // Отсеиваем методы и свойства ненужного типа доступа if (parent[i][field] == type) continue; // Отсеиваем методы и свойства // которые уже присутствуют в child if (field in child[i]) continue; // Сохраняем тип доступа child[i][field] = parent[i][field]; // Копируем метод copy(parent[1 + i], child[1 + i], field); } } }; /*-------------------------------------------------------------------------------------------------- | | -> Конструктор нового класса | |-------------------------------------------------------------------------------------------------*/ var constructor = function(params, parent) { // Название скрытого свойства // для доступа к приватным методам и свойствам var privateField = Math.random(); // Пароль для доступа к приватным методам и свойствам var pass = Math.random(); // Класс для приватных методов и свойств var Private = function() {}; // Конструктор нового класса var Self = function() { // Создаем экземпляр класса для приватных методов и свойств var _private = new Private(); // Добавляем список свойств к обьекту _private // Типы доступа: Публичные, Защищенные, Приватные extendTypes(params[3], _private); // Добавляем свойство к обьекту this // для доступа к приватным методам и свойствам // через все прототипные методы, геттеры, сеттеры и публичные свойства this[privateField] = function(val) { // Проверяем пароль if (val == pass) { // Возвращаем приватные методы и свойства return _private; } }; // Пользовательский конструктор if (typeof _private.constructor == 'function') { _private.constructor.apply(_private, arguments); } }; // Добавляем список статических методов и свойств в Self и Private extendStatic(Self, Private, params); // Добавляем список методов и свойств к прототипам классов Self и Private extendPrototype(Self, Private, params, privateField, pass); // Трейт Self['trait'] = function() { // Преобразоваем из исходного во внутренне представление // для поиска и переопределения var child = parse(arguments); // Добавляем новые или переопределяем унаследованные методы и свойства родителя // Типы доступа: Публичные, Защищенные, Приватные extend(parent, child, -1); // Преобразоваем из исходного во внутренне представление // для конечного пользования params = getParams(child); // Добавляем список методов и свойств к прототипам классов Self и Private extendPrototype(Self, Private, params, privateField, pass); }; // Наследование Self['extend'] = function() { // Преобразоваем из исходного во внутренне представление // для поиска и переопределения var child = parse(arguments); // Добавляем новые или переопределяем унаследованные методы и свойства родителя // Типы доступа: Публичные, Защищенные extend(parent, child, 2); // Возвращаем конструктор нового класса return constructor(getParams(child), child); }; // Возвращаем текущий класс return Self; }; /*-------------------------------------------------------------------------------------------------- | | -> Наследование | |-------------------------------------------------------------------------------------------------*/ return { // Для создания динамических и статических классов extend: function() { // Преобразоваем из исходного во внутренне представление // для поиска и переопределения var child = parse(arguments); // Возвращаем конструктор нового класса return constructor(getParams(child), child); }, // Для создания только статических классов static: { extend: function() { return new (CT.extend.apply(CT, arguments)); } } }; })(); //-------------------------------------------------------------------------------------------------- if (typeof module !== 'undefined' && module.exports) { module.exports = CT; } //--------------------------------------------------------------------------------------------------