Last active
April 19, 2018 04:54
-
-
Save maumchaves/573ff4a3712402105a265cfc3ade7a24 to your computer and use it in GitHub Desktop.
Functional Constructor Pattern: an inheritance pattern described by Douglas Crockford in his book "JavaScript: The Good Parts" (Ch.5).
This file contains hidden or 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
"use strict"; | |
/** | |
* var myConstructor = function(spec, shared) { ... }; | |
* | |
* ~ The 'spec' object contains all of the information that the constructor | |
* needs to make an instance (it can also be a single value). Destructuring | |
* assignment can be used for more clarity about the excpected values. | |
* | |
* ~ The 'shared' object is a container of secrets that are shared by the | |
* constructors in the inheritance chain (optional). If not passed, it is made. | |
*/ | |
var mammal = function ({name, saying}/* , { sharedValue = 'Shared' } = {} */) { | |
const that = {}; | |
that.getName = () => name; | |
that.says = () => saying; | |
/** | |
* More securely, we can define the new public methods first as private | |
* methods, and then assign them to 'that'. | |
*/ | |
// const getName = () => { | |
// return name; | |
// } | |
// that.getName = getName; | |
return that; | |
} | |
var cat = function ({name, saying}) { | |
saying = saying || 'meow'; | |
const that = mammal({name, saying}); | |
that.purr = () => 'r-r-r-r-r'; | |
that.getName = () => (that.says() + ' ' + name + ' ' + that.says()); | |
return that; | |
} | |
/** | |
* Accessing a super method | |
* ======================== | |
* | |
* Let's extend the Object prototype with a 'superior' method. This function | |
* will invoke the original method even if the property is changed (overridden). | |
* | |
* 1. We extend the Function prototype to allow us to create methods in a safety | |
* way. Also, we will remove the ugliness of writing '.prototype' every time. | |
* | |
* 2. We use our new method 'method' to extend the Object prototype. | |
*/ | |
Function.prototype.method = function (name, func) { | |
if (!this.prototype[name]) { | |
this.prototype[name] = func; | |
return this; | |
} | |
} | |
Object.method('superior', function (name) { | |
const method = this[name]; | |
return function() { | |
return method.apply(this, arguments); | |
} | |
}) | |
/** | |
* Some people claim that modifying objects that we don't own (define) is not | |
* a good practice. One reason is that it could bring compatibility issues | |
* with future versions (and bugs) if there are conflicts with the names of the | |
* properties they are using and the ones that we added. So an alternative to | |
* get the super method could be to implement a completly separate function. | |
*/ | |
// function superior(thisArg, methodName) { | |
// const method = thisArg[methodName]; | |
// return function() { | |
// return method.apply(thisArg, arguments); | |
// } | |
// } | |
var coolcat = function ({name, saying}) { | |
const that = cat({name, saying}); | |
const superGetName = that.superior('getName'); | |
that.getName = () => ('like ' + superGetName() + ' baby'); | |
return that; | |
} | |
/** | |
* Objects composition | |
* =================== | |
*/ | |
var logger = function (that) { | |
that.log = (message) => console.log('Log from logger:', message); | |
} | |
var loggerCat = function ({name, saying}) { | |
const that = coolcat({name, saying}); | |
logger(that); | |
return that; | |
} | |
/** | |
* Let's try our implementation! | |
* ============================= | |
*/ | |
var myMammal = mammal({ name: 'Herb' }); | |
var myCat = cat({ name: 'Henrietta' }); | |
var myCoolcat = coolcat({ name: 'Bix' }); | |
var myLoggerCat = loggerCat({ name: 'Charles' }); | |
console.log(myMammal.getName()); | |
console.log(myCat.getName()); | |
console.log(myCat.purr()); | |
console.log(myCoolcat.getName()); | |
myLoggerCat.log(myLoggerCat.getName()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment