Last active
December 17, 2015 13:39
-
-
Save amatiasq/5619166 to your computer and use it in GitHub Desktop.
A function to create
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Provides: | |
* Function extend(Function parent, Object config, Object statics); | |
* | |
* It extends constructors with it's methods | |
* Also provides to every method who overwrites another one | |
* with a this.base() method to invoke overwrote method. | |
* This feature is based in Dean Edwards implementation: | |
* http://dean.edwards.name/weblog/2006/03/base/ | |
* | |
* Created constructor has methods | |
* .extend(Object config) | |
* and | |
* .inject(Object config) | |
*/ | |
//jshint camelcase:false | |
/*globals module*/ | |
(function(root, undefined) { | |
'use strict'; | |
var has = Object.prototype.hasOwnProperty; | |
var _ = Object.keys ? { | |
each: function each_ECMA5(collection, callback) { | |
Object.keys(collection).forEach(callback); | |
}, | |
getOwn: function getOwn_ECMA5(target, prop) { | |
return Object.getOwnPropertyDescriptor(target, prop); | |
}, | |
get: function get_ECMA5(target, prop) { | |
var descriptor; | |
while (target) { | |
descriptor = Object.getOwnPropertyDescriptor(target, prop); | |
if (descriptor) return descriptor; | |
target = Object.getPrototypeOf(target); | |
} | |
}, | |
set: function set_ECMA5(target, prop, descriptor) { | |
Object.defineProperty(target, prop, descriptor); | |
}, | |
wrap: function wrap_ECMA5(funct, base) { | |
// HACK: Performance of the second function is NEFAST! | |
if (extend.performanceHack) { | |
return function() { | |
var a = this.base; | |
this.base = base.value; | |
// If you are here and don't know what to do, debug into the next line | |
var result = funct.apply(this, arguments); | |
this.base = a; | |
return result; | |
}; | |
} | |
base.configurable = true; | |
return function() { | |
var original = Object.getOwnPropertyDescriptor(this, 'base'); | |
Object.defineProperty(this, 'base', base); | |
// If you are here and don't know what to do, debug into the next line | |
var result = funct.apply(this, arguments); | |
Object.defineProperty(this, 'base', original || empty); | |
return result; | |
}; | |
} | |
} : { | |
each: function each_FALLBACK(collection, callback) { | |
for (var i in collection) | |
if (has.call(collection, i)) | |
callback(i); | |
}, | |
getOwn: function getOwn_FALLBACK(target, prop) { | |
return has.call(target, prop) ? { value: target[prop] } : null; | |
}, | |
get: function get_FALLBACK(target, prop) { | |
return { value: target[prop] }; | |
}, | |
set: function set_FALLBACK(target, prop, descriptor) { | |
if (descriptor) | |
target[prop] = descriptor.value; | |
}, | |
wrap: function wrap_FALLBACK(funct, base) { | |
return function() { | |
var a = this.base; | |
this.base = base.value; | |
// If you are here and don't know what to do, debug into the next line | |
var result = funct.apply(this, arguments); | |
this.base = a; | |
return result; | |
}; | |
} | |
}; | |
/** | |
* Adds every item in config to obj | |
* If it's a function it will wrap it in order to have this.base(); | |
* | |
* @param obj <Object> The object where the properties will be injected. | |
* @param config <JSON> The object with methdos to inject. | |
*/ | |
function inject(target, props) { | |
if (!props) return; | |
_.each(props, function(prop) { | |
var desc = _.getOwn(props, prop); | |
var base = _.get(target, prop); | |
if (desc && typeof desc.value === 'function' && base) | |
desc.value = _.wrap(desc.value, base); | |
_.set(target, prop, desc); | |
}); | |
} | |
function copy(target, source) { | |
_.each(source, function(prop) { | |
_.set(target, prop, _.getOwn(source, prop)); | |
}); | |
} | |
/// Dummy, just for prototype | |
function intermediate() { } | |
/** | |
* Creates a new function who's prototype property extend <Parent>'s prototype property. | |
* | |
* @param Parent <Function> The constructor of the type to extend. | |
* @param config <JSON> Object with methods to add to the new type. | |
* @returns <Function> The constructor of the new type. | |
*/ | |
function extend(Parent, config, statics) { | |
config = config || {}; | |
// We create the constructor | |
var ctor = has.call(config, 'constructor') && | |
typeof config.constructor === 'function' ? | |
_.wrap(config.constructor, Parent) : | |
function() { Parent.apply(this, arguments); }; | |
// Add basic static methods | |
ctor.extend = function(desc, statics) { | |
return extend(this, desc, statics); | |
}; | |
ctor.inject = function(desc, statics) { | |
inject(this.prototype, desc); | |
inject(this, statics); | |
return this; | |
}; | |
// Copy parent's statics | |
copy(ctor, Parent); | |
inject(ctor, statics); | |
// Extend parent prototype | |
intermediate.prototype = Parent.prototype; | |
ctor.prototype = new intermediate; | |
// Apply new methods | |
ctor.inject(config); | |
// Fix constructor | |
ctor.prototype.constructor = ctor; | |
return ctor; | |
} | |
extend.performanceHack = false; | |
if (typeof module !== 'undefined' && module.exports) | |
module.exports = extend; | |
else if (typeof define !== 'undefined' && define.amd) | |
define(function() { return extend }); | |
else | |
root.extend = extend; | |
})(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Animal = extend(Object, { | |
constructor: function(name) { | |
// Invoke Object's constructor | |
this.base(); | |
this.name = name; | |
// Log creation | |
console.log('New animal named ' + name); | |
}, | |
// Abstract | |
makeSound: function() { | |
console.log(this.name + ' is going to make a sound :)'); | |
}, | |
}); | |
var Dog = Animal.extend({ | |
constructor: function(name) { | |
// Invoke Animals's constructor | |
this.base(name); | |
// Log creation | |
console.log('Dog instanciation'); | |
}, | |
bark: function() { | |
console.log('WOF!!!'); | |
}, | |
makeSound: function() { | |
this.base(); | |
this.bark(); | |
} | |
}); | |
var pet = new Dog('buddy'); | |
// New animal named buddy | |
// Dog instanciation | |
pet.makeSound(); | |
// buddy is going to make a sound :) | |
// WOF!!! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment