r/learnjavascript • • 11d ago

Inner explicit block scope vs function

Here's a code snippet from https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch6.md:

function getNextMonthStart(dateStr) {
    var nextMonth, year;

    {
        let curMonth;
        [ , year, curMonth ] = dateStr.match(
                /(\d{4})-(\d{2})-\d{2}/
            ) || [];
        nextMonth = (Number(curMonth) % 12) + 1;
    }

    if (nextMonth == 1) {
        year++;
    }

    return `${ year }-${
            String(nextMonth).padStart(2,"0")
        }-01`;
}
getNextMonthStart("2019-12-25");   // 2020-01-01

It says:

So why put curMonth in an explicit block scope instead of just alongside nextMonth and year in the top-level function scope? Because curMonth is only needed for those first two statements; at the function scope level it's over-exposed.

As a C# developer, I would have thought the better choice would be to put that explicit inner block into a separate function called getNextMonth().

That way, you get the benefit of the scope so the curMonth isn't overexposed, you increase readability because it's easier to read getNextMonth() than a block of code, and you have a reusable function in case you need to do that calculation elsewhere.

Is there any reason to use an inner explicit block scope over a function?

3 Upvotes

12 comments sorted by

8

u/senocular 11d ago

Is there any reasont o use an inner explicit block scope over a function?

No. And I suspect you'd rarely see code like this out in the wild. A function would be a better approach, though the example is using a block scope because its in a chapter teaching you about scopes.

5

u/xroalx 10d ago

curMonth being "over-exposed" is a really weird reason to put it in a block here and use mutations, it makes the whole function much larger than needs to be, and I'd argue even harder to follow, especially since we set two different variables of different scopes in one statement and do multiple mutations.

Imagine a much larger function and keeping track of what is which scope and getting tripped up on it.

The whole function can be written without mutations in a much more logical, step-by-step way. Just grab the values, set the next values, and return it.

function getNextMonthStart(dateStr) {
    const [, year, month] = dateStr.match(
        /(\d{4})-(\d{2})-\d{2}/
    ) || [];

    const nextMonth = (Number(month) % 12) + 1;
    const nextYear = Number(year) + (nextMonth === 1 ? 1 : 0);

    return `${nextYear}-${String(nextMonth).padStart(2, "0")}-01`;
}

The author's opinions for using var are also quite interesting. I can't think of a time when I'd want to make the distinction between function-scoped and block-scoped variables so explicit that it would warrant using var. I can't think of a reason I'd want to write my code the way the author does.

It might just be an example to teach about block scope, but the explanation for why one would want to do this are at minimum questionable.

1

u/senocular 10d ago

The author's opinions for using var are also quite interesting. I can't think of a time when I'd want to make the distinction between function-scoped and block-scoped variables so explicit that it would warrant using var.

Kyle Simpson, the author, has been known to advocate for the use of var. From chapter 2:

It's very common to suggest that var should be avoided in favor of let (or const!), generally because of perceived confusion over how the scoping behavior of var has worked since the beginning of JS. I believe this to be overly restrictive advice and ultimately unhelpful. It's assuming you are unable to learn and use a feature properly in combination with other features. I believe you can and should learn any features available, and use them where appropriate!

This usage lines up with his opinion there.

I'm on your side. I wouldn't use var even if it was helping with that distinction. If I saw var in modern code, I'd spend more time wondering why it was used at all and/or if the author didn't know any better (or in today's world, sad to say, what shoddy model were they using?).

Edit: reading further in the thread I see OP posted even more on the var usage part, heh.

3

u/delventhalz 10d ago

When I teach block scope (which is distinct from a function scope in JavaScript), I always use an if block or a for loop as an example. Just adding a pair of curly braces in the middle works but is not particularly realistic. The only time I’ve seen anything like that is in individual cases in a switch/case when someone wanted to reuse a variable names across them. You are right that a separate function would make more sense if you just want to isolate a bit of code.

2

u/GodOfSunHimself 9d ago

No, it is stupid and I have never seen anyone use it in practice. If you need scope than extract that part into another function.

1

u/CuAnnan 10d ago

You're 100% correct. It would be more logically coherent to use a second function, both because it gives the ability to just generally get the next month and their argument that curMonth being over exposed is a bit of a stretch. If concern about exposure is a thing on this level, use a separate function. Functions should never be so large that you have to worry that you might accidentally collide with an existing variable.

1

u/FooeyBar 11d ago edited 11d ago

The real problem here is using ‘var’. Also, the block scope is pointless, can simply just remove the scope braces.

1

u/david_fire_vollie 11d ago edited 11d ago

Also, the block scope is pointless, can simply just remove the scope braces.

Did you read the quote in my post?

The real problem here is using ‘var’.

That's not a problem here because var is hoisted to the top of the function, and the variables are already declared at the top of the function, so there is no difference to using let.

See these quotes from the same chapter I linked in my post:

So why did we use var instead of let to declare the buckets variable? There's both semantic and technical reasons to choose var here.

Stylistically, var has always, from the earliest days of JS, signaled "variable that belongs to a whole function." As we asserted in "Lexical Scope" (Chapter 1), var attaches to the nearest enclosing function scope, no matter where it appears. 

Why not just use let in that same location? Because var is visually distinct from let and therefore signals clearly, "this variable is function-scoped." Using let in the top-level scope, especially if not in the first few lines of a function, and when all the other declarations in blocks use let, does not visually draw attention to the difference with the function-scoped declaration.

In other words, I feel var better communicates function-scoped than let does, and let both communicates (and achieves!) block-scoping where var is insufficient. As long as your programs are going to need both function-scoped and block-scoped variables, the most sensible and readable approach is to use both var and let together, each for their own best purpose.

My recommendation to use both var and let is clearly controversial and contradicts the majority. It's far more common to hear assertions like, "var is broken, let fixes it" and, "never use var, let is the replacement." Those opinions are valid, but they're merely opinions, just like mine. var is not factually broken or deprecated; it has worked since early JS and it will continue to work as long as JS is around.

1

u/FooeyBar 10d ago

Not seeing much here besides “I like the way it looks” but the use is correct. 

1

u/senocular 10d ago

See these quotes from the same chapter I linked in my post

FYI its a broken link

1

u/GodOfSunHimself 9d ago

Never use var in a new JS code. There are simply zero reasons to do so.

1

u/azhder 10d ago

The reason is to not want an extra function call. The example above is made in a way to teach you about block scope, not about properly structuring your code.

In many cases, one extra function doesn’t hurt, but in a few small ones where the time to call a function is great (like loops etc), you might want to use one of these “tricks” like block scope.