189 lines
5.3 KiB
JavaScript
189 lines
5.3 KiB
JavaScript
/**
|
|
* @module inherit
|
|
* @version 2.2.2
|
|
* @author Filatov Dmitry <dfilatov@yandex-team.ru>
|
|
* @description This module provides some syntax sugar for "class" declarations, constructors, mixins, "super" calls and static members.
|
|
*/
|
|
|
|
(function(global) {
|
|
|
|
var hasIntrospection = (function(){'_';}).toString().indexOf('_') > -1,
|
|
emptyBase = function() {},
|
|
hasOwnProperty = Object.prototype.hasOwnProperty,
|
|
objCreate = Object.create || function(ptp) {
|
|
var inheritance = function() {};
|
|
inheritance.prototype = ptp;
|
|
return new inheritance();
|
|
},
|
|
objKeys = Object.keys || function(obj) {
|
|
var res = [];
|
|
for(var i in obj) {
|
|
hasOwnProperty.call(obj, i) && res.push(i);
|
|
}
|
|
return res;
|
|
},
|
|
extend = function(o1, o2) {
|
|
for(var i in o2) {
|
|
hasOwnProperty.call(o2, i) && (o1[i] = o2[i]);
|
|
}
|
|
|
|
return o1;
|
|
},
|
|
toStr = Object.prototype.toString,
|
|
isArray = Array.isArray || function(obj) {
|
|
return toStr.call(obj) === '[object Array]';
|
|
},
|
|
isFunction = function(obj) {
|
|
return toStr.call(obj) === '[object Function]';
|
|
},
|
|
noOp = function() {},
|
|
needCheckProps = true,
|
|
testPropObj = { toString : '' };
|
|
|
|
for(var i in testPropObj) { // fucking ie hasn't toString, valueOf in for
|
|
testPropObj.hasOwnProperty(i) && (needCheckProps = false);
|
|
}
|
|
|
|
var specProps = needCheckProps? ['toString', 'valueOf'] : null;
|
|
|
|
function getPropList(obj) {
|
|
var res = objKeys(obj);
|
|
if(needCheckProps) {
|
|
var specProp, i = 0;
|
|
while(specProp = specProps[i++]) {
|
|
obj.hasOwnProperty(specProp) && res.push(specProp);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
function override(base, res, add) {
|
|
var addList = getPropList(add),
|
|
j = 0, len = addList.length,
|
|
name, prop;
|
|
while(j < len) {
|
|
if((name = addList[j++]) === '__self') {
|
|
continue;
|
|
}
|
|
prop = add[name];
|
|
if(isFunction(prop) &&
|
|
(!hasIntrospection || prop.toString().indexOf('.__base') > -1)) {
|
|
res[name] = (function(name, prop) {
|
|
var baseMethod = base[name]?
|
|
base[name] :
|
|
name === '__constructor'? // case of inheritance from plane function
|
|
res.__self.__parent :
|
|
noOp;
|
|
return function() {
|
|
var baseSaved = this.__base;
|
|
this.__base = baseMethod;
|
|
var res = prop.apply(this, arguments);
|
|
this.__base = baseSaved;
|
|
return res;
|
|
};
|
|
})(name, prop);
|
|
} else {
|
|
res[name] = prop;
|
|
}
|
|
}
|
|
}
|
|
|
|
function applyMixins(mixins, res) {
|
|
var i = 1, mixin;
|
|
while(mixin = mixins[i++]) {
|
|
res?
|
|
isFunction(mixin)?
|
|
inherit.self(res, mixin.prototype, mixin) :
|
|
inherit.self(res, mixin) :
|
|
res = isFunction(mixin)?
|
|
inherit(mixins[0], mixin.prototype, mixin) :
|
|
inherit(mixins[0], mixin);
|
|
}
|
|
return res || mixins[0];
|
|
}
|
|
|
|
/**
|
|
* Creates class
|
|
* @exports
|
|
* @param {Function|Array} [baseClass|baseClassAndMixins] class (or class and mixins) to inherit from
|
|
* @param {Object} prototypeFields
|
|
* @param {Object} [staticFields]
|
|
* @returns {Function} class
|
|
*/
|
|
function inherit() {
|
|
var args = arguments,
|
|
withMixins = isArray(args[0]),
|
|
hasBase = withMixins || isFunction(args[0]),
|
|
base = hasBase? withMixins? applyMixins(args[0]) : args[0] : emptyBase,
|
|
props = args[hasBase? 1 : 0] || {},
|
|
staticProps = args[hasBase? 2 : 1],
|
|
res = props.__constructor || (hasBase && base.prototype.__constructor)?
|
|
function() {
|
|
return this.__constructor.apply(this, arguments);
|
|
} :
|
|
hasBase?
|
|
function() {
|
|
return base.apply(this, arguments);
|
|
} :
|
|
function() {};
|
|
|
|
if(!hasBase) {
|
|
res.prototype = props;
|
|
res.prototype.__self = res.prototype.constructor = res;
|
|
return extend(res, staticProps);
|
|
}
|
|
|
|
extend(res, base);
|
|
|
|
res.__parent = base;
|
|
|
|
var basePtp = base.prototype,
|
|
resPtp = res.prototype = objCreate(basePtp);
|
|
|
|
resPtp.__self = resPtp.constructor = res;
|
|
|
|
props && override(basePtp, resPtp, props);
|
|
staticProps && override(base, res, staticProps);
|
|
|
|
return res;
|
|
}
|
|
|
|
inherit.self = function() {
|
|
var args = arguments,
|
|
withMixins = isArray(args[0]),
|
|
base = withMixins? applyMixins(args[0], args[0][0]) : args[0],
|
|
props = args[1],
|
|
staticProps = args[2],
|
|
basePtp = base.prototype;
|
|
|
|
props && override(basePtp, basePtp, props);
|
|
staticProps && override(base, base, staticProps);
|
|
|
|
return base;
|
|
};
|
|
|
|
var defineAsGlobal = true;
|
|
if(typeof exports === 'object') {
|
|
module.exports = inherit;
|
|
defineAsGlobal = false;
|
|
}
|
|
|
|
if(typeof modules === 'object') {
|
|
modules.define('inherit', function(provide) {
|
|
provide(inherit);
|
|
});
|
|
defineAsGlobal = false;
|
|
}
|
|
|
|
if(typeof define === 'function') {
|
|
define(function(require, exports, module) {
|
|
module.exports = inherit;
|
|
});
|
|
defineAsGlobal = false;
|
|
}
|
|
|
|
defineAsGlobal && (global.inherit = inherit);
|
|
|
|
})(this);
|