Микробиблиотечка для формирования классов в JavaScript. Целиком обозреть файл можно на GitHub, а ниже я его рассмотрю по порядку фрагментов.
§ 1
var WeRB = function WeRB (config) { WeRB.merge(this, config || {}); };
Формируем объект – пространство имен, а заодно и функцию – конструктор базового класса. Возможно, в дальнейшем я откажусь от совмещения этих двух ролей в одном объекте, но пока так.
§ 2
if (Object.defineProperty) { WeRB.merge = function (target, source, deep) { var props = Object.keys(source); for (var i = 0, l = props.length; i < l; i++) { var desc = Object.getOwnPropertyDescriptor(source, props[i]); if (deep) { var value = desc.value; if (value && typeof value === 'object') { if (!(value instanceof Array || value instanceof Boolean || value instanceof Date || value instanceof Error || value instanceof Function || value instanceof Number || value instanceof RegExp || value instanceof String)) { desc.value = WeRB.merge(target[props[i]] || {}, value); } } } Object.defineProperty(target, props[i], desc); } return target; }; } else { WeRB.merge = function (target, source, deep) { for (var prop in source) { if (source.hasOwnProperty(prop) && prop !== 'inherited' && prop != 'constructor' && prop != '__proto__') { var value = source[prop]; var old = target[prop]; if (deep && old && typeof value === 'object') { if (!(value instanceof Array || value instanceof Boolean || value instanceof Date || value instanceof Error || value instanceof Function || value instanceof Number || value instanceof RegExp || value instanceof String)) { value = WeRB.merge(old, value); } } target[prop] = value; } } return target; }; }
Определяем функцию, примешивающую к одному объекту перечислимые свойства другого... Тут возникает проблема со старыми браузерами — если функция совместима с ними, то в новых вместо корректного копирования геттеров и сеттеров она перенесет только значение. В данном случае я счел наилучшим выходом сделать два варианта функции.
target
- объект, в который копируются свойства;
source
- объект, из которого копируются свойства;
deep
- булев флаг, определяющий, следует ли объединять одноименные свойства источника и приемника, если это объект, исключая встроенные классы.
Возвращает функция объект-приемник.
§ 3
WeRB.mixin = function (mix) { if (typeof mix === 'function') { return this.mixin(mix.prototype); } var p = function () {}; p.prototype = this.prototype; this.prototype = new p(); WeRB.merge(this.prototype, mix); this.prototype.constructor = this; return this; };
«Подмешиваем» объект к классу. Если подмешивается функция, то берется ее прототип как класса. Хочу обратить внимание, что свойства не просто так объединяются, а вставляются отдельным пунктом в цепочку прототипов. Каждую такую примесь можно также рассматривать как анонимный класс в цепочке наследования.
§ 4
WeRB.class = function (config) { var c = config || {}; var s = c['superclass'] || this; var n = c['namespace'] || this; var m = c['mixin'] || []; var f = c['constructor'] || function () { s.apply(this, arguments); }; var p = function () {}; p.prototype = s.prototype; f.prototype = new p(); if (Object.defineProperty) { Object.defineProperty(f.prototype, 'constructor', { value : f, writeable : false, configurable : false, enumerable : false }); Object.defineProperty(f.prototype, 'inherited', { value : p.prototype, writeable : false, configurable : false, enumerable : false }); Object.defineProperty(f.prototype, '__proto__', { writeable : false, configurable : false, enumerable : false }); } else { f.prototype.constructor = f; f.prototype.inherited = p.prototype; } delete c['superclass']; delete c['namespace']; delete c['mixin']; delete c['constructor']; WeRB.merge(f.prototype, c); f.superclass = s; f.class = WeRB.class; f.mixin = WeRB.mixin; for (var i = 0, l = m.length; i < l; i++) { f.mixin(m[i]); } if (f.name && f.name != '') { n[f.name] = f; } return f; };
Создает класс по объекту-описанию. В качестве конструктора лучше передавать неанонимную функцию.
Микро-демонстрация
WeRB.class({ desc : 'DEMO', constructor : function Alpha () {} }); WeRB.Alpha.class({ get beta() { return this._beta; }, set beta(val) { console.log({set_beta : val}); this._beta = val; }, namespace : WeRB, constructor : function Beta () {} }); WeRB.class({ superclass : WeRB.Beta, constructor : function Gamma () {} }); var cx = new WeRB.Gamma(); cx.beta = 100; console.log({ a : new WeRB.Alpha(), b : new WeRB.Beta(), c : cx });
Комментариев нет:
Отправить комментарий