UNPKG

proteus

Version:

A declarative way of creating objects, properties, and classes in ES5 JavaScript

234 lines (211 loc) 6.71 kB
var putil = require("proteus/util"), merge = putil.merge, mergeAll = putil.mergeAll, slice = putil.slice, create = require("proteus/create").create, // property = require("proteus/properties").property, hasOwnProp = Object.prototype.hasOwnProperty, EMPTY = putil.EMPTY ; /** * @module Proteus * @submodule class * @main Proteus */ /** * Create a Constructor function, optionally setting its prototype to * the supplied object. * * When the Constructor function is called by 'new' it will attempt to * initialize the instance. First by calling the Constructor's "initialize" * method, passing the new instance and the arguments object. If that * returns a value, that will be returned as the new instance. Otherwise, * it will attempt to apply the instance's "init" method passing all * arguments as they were passed to the constructor function. * * @method makeCtor * @private * @param {Object} proto, optional - object to use as the prototype * @return {Function} the new constructor function */ function makeCtor (proto) { var Ctor = function Proteus () { var cFn, iFn, inst; if (arguments[0] !== EMPTY) { if ((cFn = this.constructor) && typeof cFn.initialize === "function" && (inst = cFn.initialize(this, arguments)) ) { return inst; } if (typeof (iFn = this.init) === "function") { iFn.apply(this, arguments); } } } ; if (proto) { Ctor.prototype = proto; } // property(Ctor).hidden().apply(Ctor.prototype, "constructor"); Object.defineProperty(Ctor.prototype, "constructor", { value: Ctor, configurable: true, writable: true }); return Ctor; } /** * Extend one object with the properties of another. * * If the extending object, `xtndr`, has a function named `extended`, it * will be called with one argument, the object that was extended. * * @method extend * @param {Object} xtnd - object to extend * @param {Object} rest - one, or more objects to extend with * @return {Object} the extended object */ function extend (xtnd) { var len = arguments.length, i = 1, xtndr ; for (; i < len; i++) { xtndr = arguments[i]; mergeAll(xtnd, xtndr); if (xtndr.extended === "function") { xtndr.extended(xtnd); } } return xtnd; } /** * Include properties onto the prototype of `self` from another Constructor * function or a plain object. If the `props` has a property named 'self', * the properties of 'self' will be merged into 'self' directly. * * If the supplying object, 'props', has a function named 'included' it will * be called with one argument, the 'self' Constructor function that was * just modified. * * @method include * @param {Function} self - Constructor function * @param {Function|object} props - either a Constructor function, or an * object of properties to include * @return {Function} self */ function include (self) { var hasOwn = hasOwnProp, proto = self.prototype, len = arguments.length, i = 1, props ; for (; i < len; i++) { props = arguments[i]; if (typeof props === "function") { mergeAll(proto, props.prototype); } else { if (hasOwn.call(props, "self") || hasOwn.call(props, "static")) { mergeAll(self, props.self || props["static"]); } mergeAll(proto, props); } if (typeof props.included === "function") { props.included(self); } } return self; } /** * Derive a new Constructor function from another, optionally adding the * supplied properties to its prototype. * * If the parent Constructor has a function named `inherited` it will * be called with one argument, the new Constructor function. * * @method derive * @param {Function} parent - a constructor object to derive from * @param {Object} props, optional - properties to add to the constructors * prototype * @param {Object} spec, optional - additional Object.defineProperties object * @return {Function} a constructor function */ function derive (parent, props, spec) { var Ctor = makeCtor(new parent(EMPTY)); // property(parent.prototype).hidden().apply(Ctor, "__super__"); Object.defineProperty(Ctor, "__super__", { value: parent.prototype }); extend(Ctor, parent); if (props) { include(Ctor, props); } if (spec) { Object.defineProperties(Ctor.prototype, spec); } if (typeof parent.inherited === "function") { parent.inherited(Ctor); } return Ctor; } module.exports = { derive: derive, extend: extend, include: include, /** * Base Class */ Class: derive(Object, { self: { /** * 'include' scoped to the current Constructor * * @method include * @param {Object} props - properties to include * @return {Object} */ include: function () { return include.apply( this, [this].concat(slice(arguments)) ); }, /** * 'extend' scoped to the current Constructor * * @method extend * @param {Object} xtnd - object to extend * @return {Object} the extended object */ extend: function (xtnd) { return extend(xtnd, this); }, /** * 'derive' scoped to the current constructor function * * @method derive * @param {Object} props * @param {Object} spec * @return {Function} * @api static */ derive: function (props, spec) { return derive(this, props, spec); }, /** * Utility to create an instance with an array of arguments * * @method make * @param {Array} args * @return {Object} new instance * @api static */ make: function (args) { var inst = create(this.prototype); this.apply(inst, Array.isArray(args) ? args : slice(arguments)); return inst; } } }) };