If people were abusing variable hoisting in some way, they can continue to do so, by not using `let`.
// x has not been defined or initialized anywhere
console.log(typeof x)
However, if you add a let statement after it like so: // x has not been defined or initialized here
console.log(typeof x)
let x = "foo";
typeof will fail with an exception, because the let statement changes x from an undefined variable to a new state that isn't even undefined anymore.TBH, It doesn't bother me at all. I think I can even go ahead and say the latter makes much more sense. Using the former one is abusing the weaknesses of the language that has come along with it throughout its history.
We have been whining about the bad parts of JavaScript for a long, long time and I think these changes are for the better for all of us.
Doesn't let itself introduce a new, implicit, scope? Here it seems let acts on things outside its own scope, which seems at least ugly.
It will also throw an exception every single time. So unless you're adding a variable, and then never testing the code path that hits the new line, you're probably going to catch it pretty early.
Inside a lexical scope, all matching variable names refer to the same variable. Because they are in the same lexical scope, all instances of the variable name "x" refer to the same variable.
Separate (but related) to this is the concept of where it is valid to dereference variables (this is where my knowledge breaks down - is there an accepted term for this?). Javascript says that for variables declared with "var", it is always valid to dereference them, but it might dereference to "undefined". For variables declared with "let", it is only valid to dereference variables in the lexical scope. In addition to this, it defines a "temporal dead zone", which covers the span between the start of the lexical scope and the "let" declaration. This isn't a novel thing - other languages do it, though they may use different terminology.
It's this "temporal dead zone" that you seem to be referring to when you say it's creating a new implicit scope.
It's still impossible for a let declaration to affect anything outside of its lexical scope. If it's shadowing a variable name in a parent scope, what it can do is stop variable names earlier in the scope from referring to the parent scope and make it refer to the variable in inner scope. This may seem like a problem, but most other languages will do the same thing (C++, Java, C#, Python, Ruby etc.) and I've never seen it be much of an issue.
Okay, but since `typeof x` can return "undefined", I would expect it to return `undefined` if x is no defined. Now, sometimes `typeof x` returns `undefined` if x is not defined, but other times it raises if x is no defined. That's the issue.
I too predict this is going to give people a lot of trouble. We will see.
Now, sometimes it will throw.
I mean, come on, you really don't see anything confusing in the fact that there are now two kinds of `undefined`, one kind you can call `typeof x` and it returns `undefined`, but another kind of undefined where you can't mention x at all without a throw? Like, if you can't mention undefined variables without getting a throw, how come `typeof` sometimes returns `undefined`? Ah, because some "undefined" variables you can do that with, but not others? So I guess there is more than one "kind" of "undefined" now? This is not confusing?
No, typeof /isn't/ throwing.
let x = 1;
function some_random_function(y) { }
(function() {
some_random_function(x);
let x = 2;
})();
This will throw the same exception for exactly the same reason. You're using a variable defined using "let" before the variable definition. Sod all to do with typeof.I don't see it as massively confusing that a variable declared one way behaves differently to a variable declared another way. That's the whole point of having a new way of declaring a variable.
Why isn't everyone up in arms about how confusing it is that it behaves differently here?
(function() {
var x = 1;
if (true) {
var x = 10;
}
console.log(x); // Will print 10
})();
(function() {
let x = 1;
if (true) {
let x = 10;
}
console.log(x); // Will print 1
})();
Oh no, if I use "let" it behaves differently to "var"!Its not like its throwing on unexpected data, its throwing because the program isn't constructed properly. This is the same as just about every other language.
Are you really saying we shouldn't make improvements to the language because then it would behave differently to the old, poorly designed features we're trying to deprecate?
[edit] You still can use typeof to check if something is defined without throwing on spurious inputs. The only time the code will throw is if you make changes to further down in the lexical scope. And guess what - whether you do that with var or with let you've changed the argument of typeof to refer to a different variable. Except with var it will likely silently fail in a hard to track down way, while with let it will error in an obvious place.
let data;
function sanitizeData() {
if (typeof data === 'undefined') {
// data not yet set
return;
}
// sanitize data in place
}
No matter what you put in data, "typeof data" is never going to throw an exception.The only way to get it to throw an exception is to change sanitizeData to add a variable shadowing "data" from the parent scope.
let data;
function sanitizeData() {
if (typeof data === 'undefined') {
// data not yet set
return;
}
// sanitize data in place
var data = ...;
// sanitize data in place
}
If I use var, sanitizeData doesn't do anything and I spent a while hunting the issue down. let data;
function sanitizeData() {
if (typeof data === 'undefined') {
// data not yet set
return;
}
// sanitize data in place
let data = ...;
// sanitize data in place
}
If I use let, sanitizeData throws as soon as it's called and I can track down the issue much quicker.The only time "typeof throws an exception" is if you add a let statement later in the lexical scope in a place where if you added a var statement, it would completely change the behaviour of your code in a way you almost certainly didn't want it to.
People are going to be told (rightly so) to use `let` instead of `var`. If they do so too mechanically, and their code is structured in certain ways, an expression which could never before throw an exception in Javascript, `typeof x`, now will do so.
This is not the end of the world, by any means. And it doesn't add an exception into unchanged code, but it still is a legitimate concern.
The statement `typeof x` still results in a throw. I understand that it's not `typeof` doing it.
It is still the case that before, you could write `typeof x` to see if `x` was defined in all cases, and the line you wrote, `typeof x` would never throw, for any possible state of `x`.
Now, that is no longer true. That is what people are "up in arms" about. Although as far as I know, nobody's actually taken up arms. I hope.
Being picky in an argument about exactly what is throwing does not change this situation. I am not sure what you don't understand, or if you understand everything but you feel that explaining what's really going is supposed to somehow appease people, oh, okay, now that I understand what's going on.... it still doesn't chagne the fact that before, if I wanted to know if `x` is defined, i could write `typeof x !== "undefined"`. In all cases. And the result of that statement would never be a throw. Now, if I want to know if x is defined, there are at least two kinds of "undefined", one that will be returned by `typeof x` as "undefined", and another where I am not allowed to mention `x` at all, including to do `typeof x`.
You can still write 'typeof x !=== "undefined"' and not worry about an exception being thrown. There are pretty much no situations where you're going to write legitimate code and 'typeof x !=== "undefined"' is going to throw. Any situations where it will would also break if you used var, but in much less obvious ways.
"Is this variable reference before its variable declaration" is never a question that you would ever want to ask at runtime.
I really don't think we'd be having this discussion if it had started with any statement other than "typeof x", but the function/operator (as we've tediously established) has no bearing on the behaviour exhibited.
You have variables. It's illegal to refer to a variable before its been declared in it's lexical scope. Once declared, variables start with the value "undefined" until you assign them a value. I don't see what's massively complicated about that. Its the same as in many other languages, just replace "undefined" with "null".
I have seen exactly zero blog posts about people being confused that they can't reference a variable before its been declared in other languages. I have seen dozens about people being confused by "variable hoisting". When looked at along with how it interacts with other language features, 'let' is definately less confusing than 'var'.
What would be the usecase of typeof'ing something undeclared in a block scope and then declaring the let further down?
Even more so with strict mode which does't really allow you to use undeclared variables anyway.
function a(){
setTimeout(b, 1000);
}
function b() {
setTimeout(c, 1000);
}
function c() {
}Hoisting works like this:
a();
function a() {
console.log("FOO");
}
Which might or might not be more readable, depending on your tastes.