Skip to content

Commit

Permalink
Merge branch 'release/v2.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Olical committed Sep 22, 2013
2 parents 8fc623b + 72f2e84 commit 2a5e404
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 318 deletions.
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,45 @@ This is a small script that adds the ability to inherit other functions prototyp
Due to there not being any documentation yet, you may want to read my JSDoc comments in the source. They will tell you everything you need to know. Here is a quick example to get you going anyway.

```javascript
// Create the base class
var Base = function(){};
// Create the base class.
function Base() {}

// Add a method
Base.prototype.foo = function() {
retrun '!foo!';
// Add a method.
Base.prototype.foo = function () {
return 'Base#foo';
};

// Create a sub class which inherits from base
var Sub = function(){}.inherit(Base);
// Create a sub class which inherits from base.
function Sub() {}
heir.inherit(Sub, Base);

// Create an instance of Sub and call it's method
// Mix in some functionality enhancing objects.
heir.mixin(Sub, events);
heir.mixin(Sub, pooling);

// Change the original method.
Sub.prototype.foo = function () {
return [
'Sub#foo',
this._super.foo.call(this)
].join(', ');
};

// Create an instance of Sub and call it's method.
var s = new Sub();
s.foo(); // Returns "!foo!"
s.foo(); // Returns "Sub#foo, Base#foo"
```

You can load this script into your browser using a normal script tag or AMD. You can also use node.js' `require` if you are running server side.

## Changes from v1

The `inherit` method used to work by cloning and merging multiple prototypes into one. This meant things like `instanceof` didn't work and you could get into some weird scenarios [caused by multiple inheritance][mi].

The new `inherit` uses the built in prototypical inheritance to provide a much cleaner outcome, as shown in [this post about prototypical inheritance][pi]. The major change is that you can't inherit from multiple classes any more.

If you still need to have multiple things shared between classes to avoid duplication, you can now use the `mixin` method to merge objects into your inheritance hierarchies where required.

## Downloading

You can obtain a copy by cloning this repository with git, installing through [npm][] or [bower][]. Heir is called `heir` within both package managers.
Expand All @@ -45,7 +66,7 @@ Tests are performed using Jasmine in the following browsers.

When testing in the more modern browsers, not Internet Explorer basically, I run it through the very early versions, some midrange versions and the very latest ones too. I don't just do the latest version.

Heir will always be tested and working perfectly in all of them before a release. I will not release anything I think is riddled with bugs. However, if you do spot one, please [submit it as an issue](https://github.com/Wolfy87/Heir/issues) and I will get right on it.
Heir will always be tested and working perfectly in all of them before a release. I will not release anything I think is riddled with bugs. However, if you do spot one, please [submit it as an issue][issues] and I will get right on it.

## License (MIT)

Expand All @@ -58,4 +79,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

[npm]: https://npmjs.org/
[bower]: http://bower.io/
[bower]: http://bower.io/
[issues]: https://github.com/Wolfy87/Heir/issues
[mi]: http://stackoverflow.com/questions/225929/what-is-the-exact-problem-with-multiple-inheritance
[pi]: http://oli.me.uk/2013/06/01/prototypical-inheritance-done-right/
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "heir",
"description": "Makes prototypical inheritance easy and robust",
"version": "1.0.1",
"version": "2.0.0",
"main": [
"./heir.js"
],
Expand Down
203 changes: 87 additions & 116 deletions heir.js
Original file line number Diff line number Diff line change
@@ -1,133 +1,104 @@
/**
* Heir v1.0.1 - http://git.io/F87mKg
* Heir v2.0.0 - http://git.io/F87mKg
* Oliver Caldwell
* MIT license
*/

(function () {
(function (name, root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
}
else if (typeof exports === 'object') {
module.exports = factory();
}
else {
root[name] = factory();
}
}('heir', this, function () {
/*global define,module*/
'use strict';

/**
* Works out if a variable is a true object (created with {} etc) and not an array or anything else that usually shows up as an object.
*
* @param {Mixed} chk The variable to check to see if it is an object. It must be a pure object, not even a prototype.
* @return {Boolean} True if it is a true object, false if it is anything else.
*/
function isObject(chk) {
return (chk && Object.prototype.toString.call(chk) === '[object Object]') === true;
}

/**
* Recursively merges two objects. Object `a` will be overridden by the values in object `b`.
* Please run the values through a cloning function first, this function does not try to clone them for you.
* The base object will be edited directly, please be careful!
*
* @param {Object} a The base object to merge into.
* @param {Object} b The object to merge down into object `a`.
* @return {Object} This is object `a` but merged with `b`.
*/
function merge(a, b) {
// Loop over all values in b. If they are not found in a then set them
// If both values are objects then recursively merge them
for (var key in b) {
// Make sure the value is not in __proto__ or something like that
if (b.hasOwnProperty(key)) {
// If they are both objects then merge recursively
if (isObject(a[key]) && isObject(b[key])) {
merge(a[key], b[key]);
}

// Otherwise just replace the base value
else {
a[key] = b[key];
}
var heir = {
/**
* Causes your desired class to inherit from a source class. This uses
* prototypical inheritance so you can override methods without ruining
* the parent class.
*
* This will alter the actual destination class though, it does not
* create a new class.
*
* @param {Function} destination The target class for the inheritance.
* @param {Function} source Class to inherit from.
* @param {Boolean} addSuper Should we add the _super property to the prototype? Defaults to true.
*/
inherit: function inherit(destination, source, addSuper) {
var proto = destination.prototype = heir.createObject(source.prototype);
proto.constructor = destination;

if (addSuper || typeof addSuper === 'undefined') {
proto._super = source.prototype;
}
}
},

// Return the merged object
return a;
}
/**
* Creates a new object with the source object nestled within its
* prototype chain.
*
* @param {Object} source Method to insert into the new object's prototype.
* @return {Object} An empty object with the source object in it's prototype chain.
*/
createObject: Object.create || function createObject(source) {
var Host = function () {};
Host.prototype = source;
return new Host();
},

/**
* Returns a recursive clone of the passed object.
* So when you edit the original the clone will not change.
* Used in prototypical inheritance.
* It will not clone arrays.
*
* @param {Object} orig The original object to clone.
* @return {Object} The cloned version of orig that can be edited without changing the original.
*/
function clone(orig) {
// Initialise variables
var cl = {};
var key;
/**
* Mixes the specified object into your class. This can be used to add
* certain capabilities and helper methods to a class that is already
* inheriting from some other class. You can mix in as many object as
* you want, but only inherit from one.
*
* These values are mixed into the actual prototype object of your
* class, they are not added to the prototype chain like inherit.
*
* @param {Function} destination Class to mix the object into.
* @param {Object} source Object to mix into the class.
*/
mixin: function mixin(destination, source) {
return heir.merge(destination.prototype, source);
},

// Loop over all values in the object
// If the value is an object then clone recursively
// Otherwise just copy the value
for (key in orig) {
if (orig.hasOwnProperty(key)) {
cl[key] = isObject(orig[key]) ? clone(orig[key]) : orig[key];
}
}
/**
* Merges one object into another, change the object in place.
*
* @param {Object} destination The destination for the merge.
* @param {Object} source The source of the properties to merge.
*/
merge: function merge(destination, source) {
var key;

// Return the clone
return cl;
}

/**
* Inherits other functions prototype objects into the current function.
*
* @param {Function|Function[]} parent A function which should have it's prototype cloned and placed into the current functions prototype. If you pass an array of functions they will all be inherited from.
* @param {Function} [forceFn] Optional function to use as the current function which is inheriting the other prototypes. It will default to `this`.
* @return {Function} The current function to allow chaining.
*/
function inherit(parent, forceFn) {
// Initialise variables
var fn = forceFn || this;
var i;

// If the parent variable is not a function then it must be an array
// So we have to loop over it and inherit each of them
// Remember to pass the current function instance!
if (typeof parent !== 'function') {
i = parent.length;
while (i--) {
inherit(parent[i], fn);
for (key in source) {
if (heir.hasOwn(source, key)) {
destination[key] = source[key];
}
}
}
else {
// It is not an array, it is a plain function
// Merge it's prototype into this one
merge(fn.prototype, clone(parent.prototype));
}

// Return the current function to allow chaining
return fn;
}
},

// Expose the inherit function by placing it in the Function prototype
Function.prototype.inherit = inherit;

// Create a nice little namespace to expose
var ns = {
isObject: isObject,
merge: merge,
clone: clone,
inherit: inherit
/**
* Shortcut for `Object.prototype.hasOwnProperty`.
*
* Uses `Object.prototype.hasOwnPropety` rather than
* `object.hasOwnProperty` as it could be overwritten.
*
* @param {Object} object The object to check
* @param {String} key The key to check for.
* @return {Boolean} Does object have key as an own propety?
*/
hasOwn: function hasOwn(object, key) {
return Object.prototype.hasOwnProperty.call(object, key);
}
};

// And expose everything else either via AMD or a global object
if (typeof define === 'function' && define.amd) {
define(function () {
return ns;
});
}
else if (typeof module === 'object' && module.exports) {
module.exports = ns;
}
else {
this.heir = ns;
}
}.call(this));
return heir;
}));
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "heir",
"version": "1.0.1",
"version": "2.0.0",
"description": "Makes prototypical inheritance easy and robust",
"license": "MIT",
"main": "heir.js",
Expand Down
Loading

0 comments on commit 2a5e404

Please sign in to comment.