Created
October 18, 2018 22:09
-
-
Save calvinjuarez/702db3f3759e60695e82e97193087421 to your computer and use it in GitHub Desktop.
A Pass at JS Mixins
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
/** | |
* Defines a mixin function. | |
* @param {string} [name='Mixin'] Name to show on Webkit. It will be | |
* prepended with a `+`, 'cause this | |
* is my gist. | |
* @param {function|object} props The mixin itself. If a function | |
* (class) is passed, its prototype | |
* will be set to the Base's. | |
* Otherwise, it will be set as the | |
* prototype of a new class function. | |
* @returns {function} The mixin function. | |
*/ | |
function defineMixin(name, props) { // Chrome ignores the `name`, unfortunately | |
if (typeof name !== 'string' || ! name) { | |
if (! props) props = name | |
name = '+Mixin' | |
} else { | |
name = `+${name}` | |
} | |
return Base => { | |
// Chrome exposes this as the identifier in the console when the class isn't | |
// defined with a name | |
let Mixin | |
if (typeof props === 'function') { | |
Mixin = props | |
Object.setPrototypeOf(Mixin.prototype, Base.prototype) | |
} else { | |
Mixin = class extends Base {} | |
Object.assign(Mixin.prototype, props) | |
} | |
// Webkit exposes this as the identifier in the console when the class isn't | |
// defined with a name | |
Object.defineProperty(Mixin, 'name', {value:name, writeable:false}) | |
return Mixin | |
} | |
} |
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
// Copy the function defined in defineMixin.js and all the stuff in this file | |
// into your browser's console and see it work. (Note that I've only tested | |
// on Chrome 69.0.3497.100 and Safari 12.0 (14606.1.36.1.9).) | |
let instance | |
//! BASE CLASS | |
//! -------------------------------------------------- | |
class Class { | |
say(message) { console.log(message) } | |
} | |
// Note that there are subtle differences between the technical results of the | |
// following cases, but they all get the job done. | |
//! STANDARD USE | |
//! -------------------------------------------------- | |
const mixin = defineMixin('Mixin', class { | |
sayHello() { this.say('Hello!') } | |
}) | |
class Subclass extends mixin(Class) { | |
speak() { this.sayHello() } | |
} | |
instance = new Subclass() | |
instance.speak() // → "Hello!" | |
//! PASS AN OBJECT LITERAL | |
//! -------------------------------------------------- | |
const mixinPassingObject = defineMixin('Mixin', { | |
sayHello() { this.say('Hello from an object literal!') } | |
}) | |
class SubclassPassingObject extends mixinPassingObject(Class) { | |
speak() { this.sayHello() } | |
} | |
instance = new SubclassPassingObject() | |
instance.speak() // → "Hello from an object literal!" | |
//! PASS A NAMED CLASS | |
//! -------------------------------------------------- | |
const mixinPassingNamedClassOnly = defineMixin(class Mixin { | |
sayHello() { this.say('Hello from a named class!') } | |
}) | |
class SubclassPassingNamedClassOnly extends mixinPassingNamedClassOnly(Class) { | |
speak() { this.sayHello() } | |
} | |
instance = new SubclassPassingNamedClassOnly() | |
instance.speak() // → "Hello from a named class!" | |
//! PASS AN OBJECT ONLY | |
//! -------------------------------------------------- | |
const mixinPassingObjectOnly = defineMixin({ | |
sayHello() { this.say('Hello from an (unnamed) object literal!') } | |
}) | |
class SubclassPassingObjectOnly extends mixinPassingObjectOnly(Class) { | |
speak() { this.sayHello() } | |
} | |
instance = new SubclassPassingObjectOnly() | |
instance.speak() // → "Hello from an (unnamed) object literal!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment