Skip to content

Instantly share code, notes, and snippets.

@polerin
Last active March 5, 2018 23:51
Show Gist options
  • Save polerin/44d3b859a051bcd095742f5894efc98f to your computer and use it in GitHub Desktop.
Save polerin/44d3b859a051bcd095742f5894efc98f to your computer and use it in GitHub Desktop.
Pseudo-enum "allowed values" pattern for Javascript
/**
* New pattern, pseudo-enum.
* Benefits:
* - no magic strings!
* - possible IDE completion
* - easier to track down what values are allowable (explicit list location)
* - better self documenting (container naming describes the intent of the field)
* - easy to inspect the object and see valid states in dev tools
* - possibly better on memory? Need to check references
* - feels like it would be faster, especially for large sets. Need to verify.
*
* Drawbacks:
* - probably a few more characters
* - if using int's you'd need to look up the matching state in the table.
* (though if it's not a bitwise flagging thing, use a string?)
*/
var NewFooFactory = function() {
// You can pass this in if you feel like it. Yay extension!
// It's basically an enum. I'm setting this up for bitwise flagging, but
// that's just habit.
//
// You could also make this a property of NewFooFactory to shorten & align
// usage, makes it a bit like a class constant. This would make it less
// open to extension by mixin/whatever, but that's a different issue.
var _allowedStates = {
off : 0,
open : 1,
pendingChange : 2,
locked : 4
};
return {
// Freezing it to prevent accidental change the of state value
allowedStates: Object.freeze(_allowedStates),
state : _allowedStates.off,
setState: function(newState) {
if (typeof this.allowedStates[state] === undefined) {
// For larger sets, I suspect this is a lot faster than .includes()
console.error("Attempting to set an invalid foo state: " + state);
return;
}
// You could do some bitwise toggling for flagged states, whatever.
this.state = state;
},
isState: function(queryState) {
// Again, you could do bitwise or other logic here, but for simplicity..
return (queryState === this.state);
}
doAFooThing: function() {
if (!this.isState(this.allowedStates.open)) {
console.error("Attempting to do a foo thing when object isn't open");
}
// do your foo thing.
return "a useful value. Because why not?";
}
};
};
/**
* Old pattern: array of string values
*/
var OldFooFactory = function() {
// You could still pass this in if you felt like it.
var allowedStates = [
"off",
"open",
"pendingChange",
"locked"
];
return {
// Boo magic string boo!
state : "off",
setState: function(newState) {
if (allowedStates.includes(state)) {
console.error("Attempting to set an invalid foo state: " + state);
return;
}
// Could do some bitwise toggling for flagged states, whatever.
this.state = state;
},
isState: function(queryState) {
// Again, you could do bitwise or other logic here, but for simplicity..
return (queryState === this.state);
}
doAFooThing: function() {
// Eewwww magic string. Bad enough inside the class but..
if (!this.isState("open")) {
console.error("Attempting to do a foo thing when object isn't open");
}
// do your foo thing.
return "a useful value. Because why not?";
}
};
};
// Usage examples
var oldFoo = OldFooFactory();
// Boo magic strings! And outside of its defined context too!
oldFoo.setState("open");
oldFoo.doAFooThing();
// Invalid but harder to know w/o autocomplete
oldFoo.setState("notReallyAState");
// New and shiny pattern!
var shinyFoo = NewFooFactory();
// A bit longer, but feels better?
shinyFoo.setState(shinyFoo.allowedStates.open);
shinyFoo.doAFooThing();
//invalid, but easier to track down why, also maybe IDE help?
shinyFoo.setState(shinyFoo.allowedStates.notARealState);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment