Created
December 22, 2019 13:31
-
-
Save samthecodingman/f916bf324bac4dce2b149544e255ddab to your computer and use it in GitHub Desktop.
Defines a function that allows you to overwrite/extend an existing accessor in JavaScript. Particularly useful for extending properties of Request/Response objects based on optional headers..
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
/*! extendGetter.js | Samuel Jones 2017 | MIT License | gist.github.com/samthecodingman */ | |
/** | |
* @file Extends built-in getter functions | |
* @author Samuel Jones 2017 (github.com/samthecodingman) | |
*/ | |
// Example usage: Overwrite `req.protocol` when behind a Google App Engine CDN/proxy | |
// extendGetter(req, 'protocol', function() { | |
// let xHttps = this.get('x-appengine-https'); // eslint-disable-line | |
// if (!xHttps) return; // header not present - fallback to internal getter | |
// return xHttps == 'on' ? 'https' : 'http'; | |
// }); | |
/** | |
* Injects the given getter in front of the built-in one. | |
* @param {Object} obj - The object on which to (re)define the property. | |
* @param {String} name - The name of the property to be defined or modified. | |
* @param {Function} getter - The new getter for the property being defined | |
* or modified. | |
* @return {Object} - The object that was passed to the function. | |
* @public | |
*/ | |
function extendGetter (obj, name, getter) { | |
if (typeof getter !== 'function') { | |
throw new TypeError('getter must be a function') | |
} | |
if (Object.hasOwnProperty(obj, name)) { | |
return extendGetter_(obj, name, getter) | |
} | |
let oldGetter = null | |
let proto = Object.getPrototypeOf(obj) | |
while (proto != null) { | |
let propDesc = Object.getOwnPropertyDescriptor(proto, name) | |
if (propDesc) { | |
if (propDesc.get) { | |
oldGetter = propDesc.get | |
} else if (typeof propDesc.value !== 'undefined') { | |
let v = propDesc.value | |
oldGetter = () => v | |
} | |
break | |
} | |
proto = Object.getPrototypeOf(proto) // recurse | |
} | |
if (oldGetter) { | |
return defineGetter_(obj, name, function () { | |
let v = getter.call(this) | |
return typeof v !== 'undefined' ? v : oldGetter.call(this) | |
}) | |
} else { | |
return defineGetter_(obj, name, getter) | |
} | |
} | |
module.exports = extendGetter | |
/** | |
* Defines a getter for the named property on the given object. | |
* @param {Object} obj - The object on which to define the property. | |
* @param {String} name - The name of the property to be defined or modified. | |
* @param {Function} getter - The new getter for the property being defined | |
* or modified. | |
* @return {Object} - The object that was passed to the function. | |
* @private | |
*/ | |
function defineGetter_ (obj, name, getter) { | |
return Object.defineProperty(obj, name, { | |
configurable: true, | |
enumerable: true, | |
get: getter | |
}) | |
} | |
/** | |
* Injects the given getter in front of the built-in one. | |
* @param {Object} obj - The object on which to (re)define the property. | |
* @param {String} name - The name of the property to be defined or modified. | |
* @param {Function} getter - The new getter for the property being defined | |
* or modified. | |
* @return {Object} - The object that was passed to the function. | |
* @private | |
*/ | |
function extendGetter_ (obj, name, getter) { | |
let originalGetter | |
let propDesc = Object.getOwnPropertyDescriptor(obj, name) | |
if (!propDesc) return defineGetter_(obj, name, getter) | |
if (typeof propDesc.value !== 'undefined') { | |
let val = propDesc.value | |
originalGetter = () => val | |
} else { | |
originalGetter = propDesc.get | |
} | |
// If no original getter is present, assign getter, otherwise inject. | |
propDesc.get = !originalGetter ? getter : function () { | |
let v = getter.call(this) | |
// return if getter returns something, otherwise call the original getter. | |
return typeof v !== 'undefined' ? v : originalGetter.call(this) | |
} | |
delete propDesc.value // remove value descriptor if present | |
return Object.defineProperty(obj, name, propDesc) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment