Skip to content

Instantly share code, notes, and snippets.

@getify getify/1a.js Secret
Created Nov 13, 2012

Embed
What would you like to do?
how `let` can be taxing on refactoring inside a function
/*
what this `foo` function does is arbitrary, made up, and irrelevant. it's intended to
illustrate some if-forked parsing logic for some string value, to do diff tasks based
what's in the value.
here is the version of the `foo` function using common function-global `var` style for
some temporary values, so they're available across various if-blocks in the function.
*/
function foo(bar) {
var tmp, tmp2;
if (bar) {
tmp = bar.split(";");
if (/^\d+$/) {
tmp2 = tmp[0].split("_");
if (tmp2.length > 2) {
bar = tmp2[tmp2.length-1];
}
else {
bar = null;
}
}
else {
bar = tmp[0];
}
}
if (bar) {
console.log("bar: " + bar);
}
else {
console.log("bar was invalid");
}
}
/*
now, 6 months later, someone else comes along and needs to refactor `foo`, to add some
additional parsing rules on the `bar` value. they're going to need some more if-statements.
no problem!
common function-global `var` usage doesn't add any extra effort or thought in the `foo`
refactoring.
remember, don't consider what it's like to write this `foo` from scratch, but what steps
you'd do to REFACTOR it from "1a.js". No worrying about the scope of `tmp2` is necessary
here.
compare "1a.js" to this "1b.js".
*/
function foo(bar) {
var tmp, tmp2;
if (bar) {
tmp = bar.split(";");
if (/^\d+$/) {
tmp2 = tmp[0].split("_");
}
else {
tmp2 = tmp[1].split("~");
}
if (tmp2.length > 2) {
bar = tmp2[tmp2.length-1];
}
else {
bar = null;
}
}
if (bar) {
console.log("bar: " + bar);
}
else {
console.log("bar was invalid");
}
}
/*
here is the version of the original (pre-refactored) `foo` function, but using `let` style
declaration for some temporary values.
in isolation, this style is a bit cleaner, since it keeps temporary variables `tmp` and
`tmp2` scoped to the smallest/nearest containing block necessary, instead of "polluting"
the function's main scope.
*/
function foo(bar) {
if (bar) {
let tmp = bar.split(";");
if (/^\d+$/) {
let tmp2 = tmp[0].split("_");
if (tmp2.length > 2) {
bar = tmp2[tmp2.length-1];
}
else {
bar = null;
}
}
else {
bar = tmp[0];
}
}
if (bar) {
console.log("bar: " + bar);
}
else {
console.log("bar was invalid");
}
}
/*
now, 6 months later, someone else comes along and needs to refactor `foo`, to add some
additional parsing rules on the `bar` value. they're going to need some more if-statements.
here's the `let` version refactored.
remember, don't consider what it's like to write this `foo` from scratch, but what steps
you'd do to REFACTOR it from "2a.js".
compare "2a.js" to this "2b.js".
in this case, in addition to the refactor logic you applied in the "1a.js" --> "1b.js"
conversion, you also have to realize that you need to separate the initial compound `tmp2`
declaration+initialization from "2a.js". instead, you have to insert an extra `let tmp2;`
empty declaration **in a higher level of the if-statement structure**, so `tmp2`s scope can
extend across several blocks.
THAT is the "mental tax" I'm talking about. It's obviously not terrible here. But it IS
extra mental work in the refactor.
*/
function foo(bar) {
if (bar) {
let tmp = bar.split(";");
let tmp2; // realizing you need to add this line *HERE* is the extra "mental tax"
if (/^\d+$/) {
tmp2 = tmp[0].split("_"); // we had to separate declaration and initialization here
}
else {
tmp2 = tmp[1].split("~");
}
if (tmp2.length > 2) {
bar = tmp2[tmp2.length-1];
}
else {
bar = null;
}
}
if (bar) {
console.log("bar: " + bar);
}
else {
console.log("bar was invalid");
}
}
@artiegold

This comment has been minimized.

Copy link

artiegold commented Nov 13, 2012

I have frequently wondered if the

if (something) {
   a_var = a_value;
} else {
   a_var = another_value;
}

pattern isn't just fundamentally broken. Either 'if' returning a value or the ternary '? :' operator is typically available. Why is there a long-standing opposition to using such constructs?

@getify

This comment has been minimized.

Copy link
Owner Author

getify commented Nov 14, 2012

@artiegold this particular example might have been able to use the ?: ternary operator. but in general, if/nested-if/else-if/else structures are quite useful when you're doing more than just a single assignment as a result of the condition. In the case of assignment, even with a bunch of nested logic, yes that could decompose into a several ?: ternary operators strung together, but I also think a lot of devs (myself included) don't enjoy the extra mental effort to keep operator precedence in those cases fully straight: foo = a ? b : c ? d ? e : f : g While that's certainly more terse, it's definitely a bit harder to glance at the structure and know the relationship of conditions to the final assignment value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.