-
-
Save AmyAmy/68ca8c52ec6d92da6206201e4def97d4 to your computer and use it in GitHub Desktop.
Traits in ES6 now
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
import babelHelpers from "babelHelpers"; | |
/* eslint no-underscore-dangle: ["error", { "allow": ["__traits__"] }] */ | |
/** | |
* @author based on https://gist.github.com/lukescott/36453a75c39c539f5c7d | |
* @example | |
* var Trait1 = { | |
* method1() {} | |
* }; | |
* | |
* var Trait2 = { | |
* method2() {} | |
* }; | |
* | |
* var Trait3 = addTraits({ | |
* method3() {} | |
* }, Trait2); | |
* | |
* hasTrait(Trait3, Trait2) // true | |
* hasTrait(Trait3, Trait1) // false | |
* | |
* @example | |
* class Base {} | |
* class Foo extends withTraits(Base, Trait1) {} | |
* | |
* hasTrait(Foo, Trait1) // true | |
* hasTrait(Foo, Trait2) // false | |
* | |
* @example | |
* class Foo2 extends Foo {} | |
* | |
* hasTrait(Foo2, Trait1) // true | |
* hasTrait(Foo2, Trait2) // false | |
* | |
* @example | |
* class Foo3 extends withTraits(Foo, Trait2) {} | |
* | |
* hasTrait(Foo3, Trait1) // true | |
* hasTrait(Foo3, Trait2) // true | |
* | |
* @example | |
* class Bar extends withTraits(Base, Trait3) {} | |
* | |
* hasTrait(Bar, Trait1) // false | |
* hasTrait(Bar, Trait2) // true | |
* hasTrait(Bar, Trait3) // true | |
* | |
* @example | |
* var Trait4 = { | |
* constructor() { | |
* this.prop = 4; | |
* }, | |
* method4() { | |
* console.log(this.prop); | |
* } | |
* } | |
* class Baz extends withTraits(Base, Trait4) {} | |
* new Baz().method4(); // 4 | |
*/ | |
/** | |
* Add traits into an Object (another trait) | |
* @param {Object} object The object (can be another trait) to add traits to. | |
* @param {Array.<Object>} traits An array of traits to add. | |
* @returns {Object} The original object, now with the added traits. | |
*/ | |
function addTraits(object, ...traits) { | |
const t = []; | |
if (object.__traits__) { | |
Array.prototype.push.apply(t, object.__traits__); | |
} | |
const props = { | |
__traits__: { | |
value: t, | |
writable: true, | |
configurable: true | |
} | |
}; | |
for (let trait of traits) { | |
if (trait.__traits__) { | |
Array.prototype.push.apply(t, trait.__traits__); | |
} | |
for (let name of Object.getOwnPropertyNames(trait)) { | |
if (name === "constructor") { | |
continue; | |
} | |
if (name === "__traits__") { | |
continue; | |
} | |
if (object.hasOwnProperty(name)) { | |
continue; | |
} | |
props[name] = { | |
value: trait[name], | |
writable: true, | |
configurable: true | |
}; | |
} | |
t.push(trait); | |
} | |
Object.defineProperties(object, props); | |
return object; | |
} | |
/** | |
* Adds traits to a class. | |
* @param {Function} baseClass The class to add the traits to. | |
* @param {Array.<Object>} traits One or more traits to add to the class. | |
* @returns {Function} A class which extends baseClass, and has all the specified traits. | |
* @example class Foo extends withTraits(Base, Trait1) {} | |
*/ | |
function withTraits(baseClass, ...traits) { | |
let TraitedClass; | |
if (typeof baseClass === "undefined") { | |
TraitedClass = class { | |
constructor() { | |
for (let trait of traits) { | |
if (trait.constructor) { | |
trait.constructor.apply(this, arguments); | |
} | |
} | |
} | |
}; | |
} | |
else { | |
TraitedClass = class extends baseClass { | |
constructor() { | |
super(...arguments); | |
for (let trait of traits) { | |
if (trait.constructor) { | |
trait.constructor.apply(this, arguments); | |
} | |
} | |
} | |
}; | |
} | |
addTraits.apply(this, [TraitedClass.prototype, ...traits]); | |
return TraitedClass; | |
} | |
/** | |
* Checks to see if a class or instance has a trait | |
* @param {Function|Object} object The class or instance to check. | |
* @param {Object} trait The trait to check. | |
* @returns {Boolean} True if the object has the specified trait. | |
*/ | |
function hasTrait(object, trait) { | |
const traits = (typeof object === "function") ? object.prototype.__traits__ : object.__traits__; | |
return Array.isArray(traits) && traits.indexOf(trait) >= 0; | |
} | |
/** | |
* Checks to see if a class or instance has all of the specified traits. | |
* @param {Function|Object} object The class or instance to check. | |
* @param {Object} trait One or more traits to check. | |
* @returns {Boolean} True if the object has all of the specified traits. | |
*/ | |
function hasAllTraits(object, ...traits) { | |
return traits.every(trait => hasTrait(object, trait)); | |
} | |
/** | |
* Checks to see if a class or instance has any of the specified traits. | |
* @param {Function|Object} object The class or instance to check. | |
* @param {Object} trait One or more traits to check. | |
* @returns {Boolean} True if the object has any of the specified traits. | |
*/ | |
function hasAnyTraits(object, ...traits) { | |
return traits.some(trait => hasTrait(object, trait)); | |
} | |
export default { | |
addTraits, | |
withTraits, | |
hasTrait, | |
hasAllTraits, | |
hasAnyTraits | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment