2

In his article on let and const Jason Orendorff states the following:

Crunchy performance details: In most cases, you can tell whether the declaration has run or not just by looking at the code, so the JavaScript engine does not actually need to perform an extra check every time the variable is accessed to make sure it’s been initialized. However, inside a closure, it sometimes isn’t clear. In those cases the JavaScript engine will do a run-time check. That means let can be a touch slower than var.

I decided to try and find an example where this held true and was stumped.

For example, let us look at the following torture scenario:

function doSomethingDumb(q) { function crash() { ++x; } q.fn = crash; crash(); let x; return crash; } 

Even though the closure is returned at the end of the function, it is guaranteed that the return statement will never execute, even though x is assigned to a member of q (and thus may escape into the wild) x will never be initialized and thus crash will always crash.

In what case would it be impossible to tell whether the variable had been initialized?

4
  • crash() won't even be executed, it already crashes when you try to assign x to q.fn. Commented Apr 11, 2017 at 1:11
  • That was supposed to be q.fn = crash. Thanks for catching that. Commented Apr 11, 2017 at 1:12
  • Btw, can you link the article with that quote? Commented Apr 11, 2017 at 1:17
  • 1
    The deed is done. Commented Apr 11, 2017 at 1:19

2 Answers 2

3

Just put that into a condition that is only sometimes fulfilled:

function example() { if (Math.random() < 0.33) tryIt(); const x = 5; if (Math.random() < 0.5) tryIt(); function tryIt() { console.log(x); } } 

In this example I've chosen a random, but it could as well depend on the function's input parameters. In general, it is not statically decidable whether the statement that accesses the variable will be executed before the initialisation - that's the halting problem. You can write a sophisticated analyser that can determine this for many cases, but there's always a trade off between sophistication and overhead of the analyser.

Sign up to request clarification or add additional context in comments.

1 Comment

@BekimBacaj The question was "Here's an example, and it's quite clear whether x is initialised or not when accessed; what is a case where that is not clear?", and I've shown an example where it's not so easy to see and that it is cannot be determined in every case.
0

There is no such thing as slow or fast handle, i.e.: named value, be it a var a const or a let. There's no such thing as "unable to determine" if they are initialized either. Moreover let/s don't get initialized - they simply get assigned. And since they are made to act similar to 'undeclared variables' they will be assigned their value at runtime and exactly at their point declaration.

Therefore, if a stop error happens occur just one line above the let declaration. It will remain fully undeclared. An you will know it just by looking at the code.

6 Comments

You might want to take a look at this for what "declaration" and "initialisation" mean for let/const.
Hm, they don't behave similar to undeclared variables at all imo. And yes, while it is always possible for the interpreter to determine (test) whether the variable is already initialised or not, we are talking about the compiler optimisation that determines whether such a test is necessary or not. Which is not decidable.
There's a bug \ wrong implementation and a wrong behavior of varletconst in a conditional \ block \ local scope obstructing the look-up principle after a failed look-around as in: function lookupDisabled(){ const myval = 1; console.log( myval ); if(myval === 1) { /*console.log( myval ); ReferenceError*/ const myval = 2; console.log( myval ); if( myval === 2 ){ /*console.log( myval ); ReferenceError*/ const myval = 3; console.log( myval );} console.log( myval ); } return myval; } where the commented logs of myval should be able to look-up in the outerscope an log 1.
No, this is neither a bug nor wrong. Please check my answer for how this works. And it's very much on purpose.
Any counterintuitive, unexpected, therefore wrong behavior; especially the ones that go against a fundamental programming principle of a given language is by definition and in general considered a bug. This one is breaking the look-up principle without a reason. This is a premature & wrong look-ahead inclusion optimization at parse time (which might spare a few cpu cycles), but nonetheless, a bug. Just because the identifier of a given value is waiting to be declared, should not be a reason to make it unreachable, especially not when a conditional creating the current context says !0.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.