Last active
January 4, 2020 20:29
-
-
Save christroutner/3dd1e0155a76d0f38d084402aca24020 to your computer and use it in GitHub Desktop.
Standard JavaScript Function Layout
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
/* | |
This gist captures the basic pattern of a JavaScript function. It emcompasses patterns for best practices. | |
*/ | |
// This global is used to reference the library instance when the 'this' context has been lost. | |
let _this | |
// With the switch to Classes for instantiating JS libraries, this is a common pattern: | |
// Note: An object starting with a capital letter denotes a Class: e.g. SomeExternalLibrary | |
const SomeExternalLibrary = require('./someExternalLibrary') | |
const someExternalLibrary = new SomeExternalLibrary() | |
// Objects with ALL_CAPITAL letters indicate a constant. | |
const SOME_CONSTANT = 4 | |
// Encompasing libraries as a Class makes writing unit tests much easier. | |
class JSLib { | |
constructor() { | |
// Maintain reference to the instance of the library when context of 'this' has been lost, | |
// such as in a function call-back. | |
_this = this | |
// Use locally referenced libraries like this, rather than the global variable. | |
// This makes it easy to stub out external library functions using Sinon, | |
// when writing unit tests. | |
this.someExternalLibrary = someExernalLibrary | |
} | |
// Any time you can break large functions into smaller functions, it's a good idea. | |
// This example function will give an example of the basic layout pattern to follow. | |
async subFunction(inObj) { | |
// The entire inner function should be wrapped in a try/catch statement. | |
try { | |
// Creating a return value and setting it to a negative result means that the | |
// output of your function is predictable. If nothing else works, you'll at | |
// least know that your function will be returning a known value. | |
let returnVal = false | |
// Passing a single input object is handy because it's easy to expand for additional | |
// functionality later, without impacting current functionality. | |
const {var1, var2, var3} = inObj | |
// The first section of any function should be the 'input validation' section. If | |
// the inputs are out of range, then you can throw an Error. These should also | |
// be the first unit tests written. | |
if(typeof(var1) !== 'string') throw new Error(`var1 must be a string`) | |
if(var2 < 0) throw new Error(`var2 must be a positive integer`) | |
// After input validation, do the meat of the work, e.g. the business logic. | |
if(var1 === 'a') { | |
returnVal = 'This is the output for a' | |
// A more complicated code path. | |
} else { | |
const preppedObj = var2 + var3 + SOME_CONSTANT | |
// Using _this means you don't have to remember what the context of 'this' is. | |
// Also alows easy stubbing of 'usefulFunc' with Sinon when writing unit tests. | |
const result = await _this.someExternalLibrary.usefulFunc(preppedObj) | |
// Exceptions or unexpected behavior should be handled by throwing an error with | |
// an informative and unique message. | |
if(!result) throw new Error(`usefulFunc() returned unexpected result`) | |
returnVal = result | |
} | |
// Return the variable you set at the top. If nothing in between has changed its state, | |
// the default negative value is assured. | |
return returnVal | |
} catch(err) { | |
// Optional. This console.error statement will probably be commented out for production, | |
// or added to a logging system like Winston (instead of the console). | |
// But when developing and debugging, it's great to follow a chain of console logs like | |
// this to quickly see where the error originated. | |
console.error(`Error in someExternalLibrary.js/subFunction()`) | |
// The error should be thrown at the end, unless there is a specific reason not to. | |
// This lets errors 'bubble up' to parent functions when they are not handled | |
// in the child functions. | |
throw err | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment