Sorry, the diff of this file is too big to display
+1
-1
| { | ||
| "name": "heir", | ||
| "description": "Makes prototypical inheritance easy and robust", | ||
| "version": "1.0.1", | ||
| "version": "2.0.0", | ||
| "main": [ | ||
@@ -6,0 +6,0 @@ "./heir.js" |
+87
-116
| /** | ||
| * Heir v1.0.1 - http://git.io/F87mKg | ||
| * Heir v2.0.0 - http://git.io/F87mKg | ||
| * Oliver Caldwell | ||
@@ -7,128 +7,99 @@ * 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; | ||
| } | ||
| 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; | ||
| } | ||
| }, | ||
| /** | ||
| * 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]); | ||
| } | ||
| /** | ||
| * 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(); | ||
| }, | ||
| // Otherwise just replace the base value | ||
| else { | ||
| a[key] = b[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); | ||
| }, | ||
| // Return the merged object | ||
| return a; | ||
| } | ||
| /** | ||
| * 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; | ||
| /** | ||
| * 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; | ||
| // 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]; | ||
| for (key in source) { | ||
| if (heir.hasOwn(source, key)) { | ||
| destination[key] = source[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); | ||
| } | ||
| /** | ||
| * 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); | ||
| } | ||
| 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 | ||
| }; | ||
| // 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; | ||
| })); |
+1
-1
| { | ||
| "name": "heir", | ||
| "version": "1.0.1", | ||
| "version": "2.0.0", | ||
| "description": "Makes prototypical inheritance easy and robust", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+35
-11
@@ -8,16 +8,29 @@ # Heir | ||
| ```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" | ||
| ``` | ||
@@ -27,2 +40,10 @@ | ||
| ## 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 | ||
@@ -50,3 +71,3 @@ | ||
| 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. | ||
@@ -64,2 +85,5 @@ ## License (MIT) | ||
| [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/ |
+105
-187
@@ -5,3 +5,2 @@ (function () { | ||
| // Set up the Jasmine environment | ||
| var jasmineEnv = jasmine.getEnv(); | ||
@@ -18,231 +17,150 @@ jasmineEnv.updateInterval = 1000; | ||
| // Configure the tests | ||
| describe('heir.isObject', function() { | ||
| it('returns true on objects', function() { | ||
| expect(heir.isObject({})).toBe(true); | ||
| var test = {}; | ||
| expect(heir.isObject(test)).toBe(true); | ||
| }); | ||
| describe('heir.createObject', function () { | ||
| it('returns a new empty object when passed one', function () { | ||
| var source = {}; | ||
| var result = heir.createObject(source); | ||
| it('returns false on a regex', function() { | ||
| expect(heir.isObject(/reg[ex]/i)).toBe(false); | ||
| var test = /reg[ex]/i; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| expect(result).toEqual({}); | ||
| expect(result).not.toBe(source); | ||
| }); | ||
| it('returns false on arrays', function() { | ||
| expect(heir.isObject([])).toBe(false); | ||
| var test = []; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| }); | ||
| it('inserts the source object into the prototype', function () { | ||
| var source = { | ||
| foo: true | ||
| }; | ||
| var result = heir.createObject(source); | ||
| it('returns false on null', function() { | ||
| expect(heir.isObject(null)).toBe(false); | ||
| var test = null; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| expect(result).toEqual(source); | ||
| expect(result).not.toBe(source); | ||
| expect(result.hasOwnProperty('foo')).toBe(false); | ||
| expect(result.foo).toBe(true); | ||
| }); | ||
| it('returns false on numbers', function() { | ||
| expect(heir.isObject(50)).toBe(false); | ||
| var test = 50; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| }); | ||
| it('returns false on strings', function() { | ||
| expect(heir.isObject('foo')).toBe(false); | ||
| var test = 'foo'; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| }); | ||
| it('returns false on booleans', function() { | ||
| expect(heir.isObject(true)).toBe(false); | ||
| var test = true; | ||
| expect(heir.isObject(test)).toBe(false); | ||
| }); | ||
| }); | ||
| describe('heir.merge', function() { | ||
| it('copies in values that did not exist', function() { | ||
| var chk = heir.merge({foo:true}, {bar: true}); | ||
| expect(chk).toEqual({foo:true, bar:true}); | ||
| }); | ||
| describe('heir.inherit', function () { | ||
| it('causes a class to inherit a method', function () { | ||
| var Source = function () {}; | ||
| Source.prototype.foo = function () {}; | ||
| it('copies in values that did exist', function() { | ||
| var chk = heir.merge({foo:true, bar:true}, {foo:false}); | ||
| expect(chk).toEqual({foo:false, bar:true}); | ||
| }); | ||
| var Destination = function () {}; | ||
| heir.inherit(Destination, Source); | ||
| Destination.prototype.bar = function() {}; | ||
| it('merges objects recursively', function() { | ||
| var chk = heir.merge({ | ||
| nest: { | ||
| foo: true, | ||
| bar: true | ||
| } | ||
| }, { | ||
| nest: { | ||
| bar: false | ||
| } | ||
| }); | ||
| var result = new Destination(); | ||
| expect(chk).toEqual({ | ||
| nest: { | ||
| foo: true, | ||
| bar: false | ||
| } | ||
| }); | ||
| expect(Destination.prototype.hasOwnProperty('foo')).toBe(false); | ||
| expect(Destination.prototype.hasOwnProperty('bar')).toBe(true); | ||
| expect(result.foo).toBeDefined(); | ||
| expect(result.bar).toBeDefined(); | ||
| }); | ||
| }); | ||
| describe('heir.clone', function() { | ||
| it('clones objects', function() { | ||
| var orig = { | ||
| foo: true, | ||
| bar: false | ||
| it('can have methods overridden', function () { | ||
| var Source = function () {}; | ||
| Source.prototype.foo = function () { | ||
| return 'Source#foo'; | ||
| }; | ||
| var cl = heir.clone(orig); | ||
| var Destination = function () {}; | ||
| heir.inherit(Destination, Source); | ||
| Destination.prototype.foo = function() { | ||
| return [ | ||
| 'Destination#foo', | ||
| Source.prototype.foo.call(this) | ||
| ].join(', '); | ||
| }; | ||
| cl.bar = true; | ||
| var source = new Source(); | ||
| var destination = new Destination(); | ||
| expect(orig).toEqual({ | ||
| foo: true, | ||
| bar: false | ||
| }); | ||
| expect(cl).toEqual({ | ||
| foo: true, | ||
| bar: true | ||
| }); | ||
| expect(source.foo()).toBe('Source#foo'); | ||
| expect(destination.foo()).toBe('Destination#foo, Source#foo'); | ||
| }); | ||
| it('clones objects recursively', function() { | ||
| var orig = { | ||
| foo: true, | ||
| bar: false, | ||
| baz: { | ||
| one: 100, | ||
| two: 300 | ||
| } | ||
| }; | ||
| it('is correct in the eyes of instanceof', function () { | ||
| var Source = function () {}; | ||
| var Destination = function () {}; | ||
| heir.inherit(Destination, Source); | ||
| var cl = heir.clone(orig); | ||
| var source = new Source(); | ||
| var destination = new Destination(); | ||
| cl.bar = true; | ||
| cl.baz.two = 200; | ||
| cl.baz.three = 300; | ||
| expect(source instanceof Source).toBe(true); | ||
| expect(source instanceof Destination).toBe(false); | ||
| expect(orig).toEqual({ | ||
| foo: true, | ||
| bar: false, | ||
| baz: { | ||
| one: 100, | ||
| two: 300 | ||
| } | ||
| }); | ||
| expect(cl).toEqual({ | ||
| foo: true, | ||
| bar: true, | ||
| baz: { | ||
| one: 100, | ||
| two: 200, | ||
| three: 300 | ||
| } | ||
| }); | ||
| expect(destination instanceof Source).toBe(true); | ||
| expect(destination instanceof Destination).toBe(true); | ||
| }); | ||
| }); | ||
| describe('heir.inherit', function() { | ||
| it('inherits a class', function() { | ||
| var Base = function(){}; | ||
| Base.prototype.foo = function() { | ||
| return '!foo!'; | ||
| }; | ||
| it('has a reference to the parent in this._super', function () { | ||
| var Source = function () {}; | ||
| var Destination = function () {}; | ||
| heir.inherit(Destination, Source); | ||
| var Base2 = function(){}; | ||
| Base2.prototype.baz = function() { | ||
| return '!baz!'; | ||
| }; | ||
| var result = new Destination(); | ||
| var Sub = function(){}.inherit(Base); | ||
| Sub.prototype.bar = function() { | ||
| return '!bar!'; | ||
| }; | ||
| Sub.inherit(Base2); | ||
| expect(result._super).toBe(Source.prototype); | ||
| }); | ||
| var b = new Base(); | ||
| var b2 = new Base2(); | ||
| var s = new Sub(); | ||
| it('can have the addition of this._super disabled', function () { | ||
| var Source = function () {}; | ||
| var Destination = function () {}; | ||
| heir.inherit(Destination, Source, false); | ||
| expect(b.bar).not.toBeDefined(); | ||
| expect(b2.bar).not.toBeDefined(); | ||
| expect(b.foo()).toEqual('!foo!'); | ||
| expect(b2.baz()).toEqual('!baz!'); | ||
| expect(s.foo()).toEqual('!foo!'); | ||
| expect(s.bar()).toEqual('!bar!'); | ||
| expect(s.baz()).toEqual('!baz!'); | ||
| var result = new Destination(); | ||
| expect(result._super).toBeUndefined(); | ||
| }); | ||
| }); | ||
| it('inherits multiple classes', function() { | ||
| var Base = function(){}; | ||
| Base.prototype.foo = function() { | ||
| return '!foo!'; | ||
| describe('heir.mixin', function () { | ||
| it('can mix methods into a class', function () { | ||
| var source = { | ||
| foo: function () {}, | ||
| bar: function () {} | ||
| }; | ||
| var Destination = function () {}; | ||
| heir.mixin(Destination, source); | ||
| var result = new Destination(); | ||
| var Base2 = function(){}; | ||
| Base2.prototype.baz = function() { | ||
| return '!baz!'; | ||
| }; | ||
| expect(result.foo).toBeDefined(); | ||
| expect(result.bar).toBeDefined(); | ||
| expect(Destination.prototype.hasOwnProperty('foo')).toBe(true); | ||
| expect(Destination.prototype.hasOwnProperty('bar')).toBe(true); | ||
| }); | ||
| }); | ||
| var Sub = function(){}.inherit([Base, Base2]); | ||
| Sub.prototype.bar = function() { | ||
| return '!bar!'; | ||
| describe('heir.hasOwn', function () { | ||
| it('returns true when it is it\'s own', function () { | ||
| var source = { | ||
| foo: true | ||
| }; | ||
| var b = new Base(); | ||
| var b2 = new Base2(); | ||
| var s = new Sub(); | ||
| expect(b.bar).not.toBeDefined(); | ||
| expect(b2.bar).not.toBeDefined(); | ||
| expect(b.foo()).toEqual('!foo!'); | ||
| expect(b2.baz()).toEqual('!baz!'); | ||
| expect(s.foo()).toEqual('!foo!'); | ||
| expect(s.bar()).toEqual('!bar!'); | ||
| expect(s.baz()).toEqual('!baz!'); | ||
| expect(heir.hasOwn(source, 'foo')).toBe(true); | ||
| }); | ||
| it('does not have to be called through the prototype', function() { | ||
| var Base = function(){}; | ||
| Base.prototype.foo = function() { | ||
| return '!foo!'; | ||
| }; | ||
| it('returns false when it is either undefined or up the prototype chain', function () { | ||
| var noProperty = {}; | ||
| var inChain = heir.createObject({ | ||
| foo: true | ||
| }); | ||
| var Base2 = function(){}; | ||
| Base2.prototype.baz = function() { | ||
| return '!baz!'; | ||
| }; | ||
| expect(heir.hasOwn(noProperty, 'foo')).toBe(false); | ||
| expect(inChain.foo).toBeDefined(); | ||
| expect(heir.hasOwn(inChain, 'foo')).toBe(false); | ||
| }); | ||
| }); | ||
| var Sub = function(){}; | ||
| heir.inherit([Base, Base2], Sub); | ||
| Sub.prototype.bar = function() { | ||
| return '!bar!'; | ||
| }; | ||
| describe('heir.merge', function () { | ||
| it('merges one object into another', function () { | ||
| var a = {foo:true}; | ||
| var b = {bar:true}; | ||
| heir.merge(a, b); | ||
| var b = new Base(); | ||
| var b2 = new Base2(); | ||
| var s = new Sub(); | ||
| expect(b.bar).not.toBeDefined(); | ||
| expect(b2.bar).not.toBeDefined(); | ||
| expect(b.foo()).toEqual('!foo!'); | ||
| expect(b2.baz()).toEqual('!baz!'); | ||
| expect(s.foo()).toEqual('!foo!'); | ||
| expect(s.bar()).toEqual('!bar!'); | ||
| expect(s.baz()).toEqual('!baz!'); | ||
| expect(a.foo).toBe(true); | ||
| expect(a.bar).toBe(true); | ||
| expect(b.foo).toBeUndefined(); | ||
| }); | ||
| }); | ||
| // Run Jasmine | ||
| jasmineEnv.execute(); | ||
| }.call(this)); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
89511
555.37%8
14.29%85
39.34%251
-28.29%1
Infinity%