On JavaScript's Weirdness
61–70 of 85 posts
Re: On JavaScript's Weirdness
#62I can pretty confidently say that every article I have seen in the past 5 years that complain about weirdness of JavaScript is about things that you would never do in a modern, production-level codebase. Many of these issues are about pre-ES6, outdated practice (e.g. eval, using == operator) that are almost certainly going to be flagged by a linter. Very occasionally, you do get hit by some weirdness, but likely does…
Maybe it is outdated practice but I still use == operator for null or undefined check. if (value == null)
In general, the whole `==` versus `===` is a silly argument in a typescript codebase, because if you know the types of the arguments, `==` behaves predictably.
Re: On JavaScript's Weirdness
#63Many mistakes in section 2. The author seems to fundamentally misunderstand block scoping vs lexical scoping, and interactions when deferring execution to the next run of the event loop. In the first example: for (let i = 0; i console.log(i)); } // prints "0 1 2" — as expected let i = 0; for (i = 0; i console.log(i)); } // prints "3 3 3" — what? i's scope is outside the for loop in the second example, and the setTime…
What's the mistake that I made there? I just didn't explain why it happens. I briefly mentioned this in the later paragraphs — it makes sense to some people, but not to most.
Re: On JavaScript's Weirdness
#64'v2';
At the top of every file could let us eliminate all this 20-year old cruft (like document.all hacks to support Internet Explorer).
Yet, despite the already established `use strict`; (which is basically 'v1.5'), the community seems completely against modernizing the language.
Re: On JavaScript's Weirdness
#65Yes, it can find some cases, but in general would require solving the halting problem.
Re: On JavaScript's Weirdness
#66Earlier quoted context omitted.
If the language needs a linter to keep otherwise-competent developers from introducing potentially maddening bugs into the codebase, it's weird. JS was developed in a hurry and has been extended into doing things it was never meant to do, and it shows.
If weird means out of norm, that's not the case. Every language either has a linter or would benefit from a linter because all languages have warts or idiosyncratic behavior. Some languages are so error-prone and hard to use they even ship with static analysis and type-checking built in, like Rust and C! (And they still have linters on top of that!) Perhaps post a language you think is exceptional to this?
Given how accessible and widespread it is, it's hard not to make mistakes in JS without a linter.
Can you shoot your foot off with C or Rust? Sure. But they're systems programming languages for the most part, and aren't realistically proposed as tools in the "move fast and break things" world of web development where JS rules supreme. Ruby and Python are also used in web dev, albeit on the back-end, and they're not as idiosyncratic as JS.
Re: On JavaScript's Weirdness
#67Many mistakes in section 2. The author seems to fundamentally misunderstand block scoping vs lexical scoping, and interactions when deferring execution to the next run of the event loop. In the first example: for (let i = 0; i console.log(i)); } // prints "0 1 2" — as expected let i = 0; for (i = 0; i console.log(i)); } // prints "3 3 3" — what? i's scope is outside the for loop in the second example, and the setTime…
Yes, it's about block scoping — but that doesn't make it less weird . In most languages this doesn't really make sense — a variable is a piece of memory, and a reference refers to it. JavaScript doesn't work like that, and that's weird to many. What's the mistake that I made there? I just didn't explain why it happens. I briefly mentioned this in the later paragraphs — it makes sense to some people, but not to most.
Re: On JavaScript's Weirdness
#68Earlier quoted context omitted.
In JavaScript, a 'let' inside the initializer of a for loop is captured by value, all the others are captured by reference. I think it's fair to call that semantics "implicit".
No, that's a mistake in the article. The variable is still captured by reference, but `let` is causing it to be re-declared on every iteration of the loop, not mutated. The following code prints 1, 2, 3. It wouldn't do that if the variable was captured by value. for (let i = 0; i console.log(i)); i++; }
Re: On JavaScript's Weirdness
#69Many mistakes in section 2. The author seems to fundamentally misunderstand block scoping vs lexical scoping, and interactions when deferring execution to the next run of the event loop. In the first example: for (let i = 0; i console.log(i)); } // prints "0 1 2" — as expected let i = 0; for (i = 0; i console.log(i)); } // prints "3 3 3" — what? i's scope is outside the for loop in the second example, and the setTime…
Yes, it's about block scoping — but that doesn't make it less weird . In most languages this doesn't really make sense — a variable is a piece of memory, and a reference refers to it. JavaScript doesn't work like that, and that's weird to many. What's the mistake that I made there? I just didn't explain why it happens. I briefly mentioned this in the later paragraphs — it makes sense to some people, but not to most.
> JS loops pretend their variables are captured by value
This has to do with how for loops work with iterators, but also what `let` means in variable declaration. You talk about 'unrolling a for loop' but what you're doing is 'attempting to express the same loop with while'. Unrolling would look like this;
// original:
for (let i = 0; i console.log(i)) }
// unrolled:
{ let i = 0; setTimeout(()=>console.log(i)) };
{ let i = 1; setTimeout(()=>console.log(i)) };
{ let i = 2; setTimeout(()=>console.log(i)) };
// original:
let i = 0;
for (i = 0; i console.log(i)) };
// unrolled:
let i = 0;
{ i = 0; setTimeout(()=>console.log(i)); };
{ i = 1; setTimeout(()=>console.log(i)); };
{ i = 2; setTimeout(()=>console.log(i)); };
Now you can begin to explain what's going wrong in the second example; 'i' is declared with 'let' outside of the block, and this means the callback passed to the setTimeout is placed in the next stack frame, but references i from the outer scope, which is modified by the time the next stack frame is running.In the original example, a different 'i' is declared inside each block and the callback passed to setTimeout references the 'i' from its scope, which isn't modified in adjacent blocks. It's confusing that you're making this about how loops work when understanding what the loop is doing is only one part of it; understanding scoping and the event loop are 2 other important pieces here.
And then if you're going to compare a while loop to a for loop, I think a critical piece is that 'while' loops (as well as 'do .. while') take only expressions in their condition, and loop until the expression is false.
'for' loops take three-part statements, the first part of which is an initialization assignment (for which 'var' and 'let' work differently), and the second of which is an expression used as the condition. So you can declare a variable with 'let' in the initialization and modify it in the 'afterthought' (the third part of the statement), but it will be treated as if each iteration of the loop is declaring it within the block created for that iteration.
So yes, there are some 'for' loop semantics that are specific to 'for' loops, but rather than explain that, you appear to be trying to make a point about loops in general that I'm not following.
I'm not saying the examples won't help people avoid pitfalls with for and while loops, but I do think they'll be unable to generalize any lessons they take away to other situations in JS, since you're not actually explaining the principles of JS at play.
Re: On JavaScript's Weirdness
#70Many mistakes in section 2. The author seems to fundamentally misunderstand block scoping vs lexical scoping, and interactions when deferring execution to the next run of the event loop. In the first example: for (let i = 0; i console.log(i)); } // prints "0 1 2" — as expected let i = 0; for (i = 0; i console.log(i)); } // prints "3 3 3" — what? i's scope is outside the for loop in the second example, and the setTime…
In short I'm not sure that they have misunderstood the scoping, they have probably understood it fine, they have remarked on the weirdness that different aspects of JavaScript enables.
Certainly with perfect understanding and knowledge of a language that you do not have to think about at all because it is so perfectly remembered nothing would ever be weird, it is the incidental behaviors of the language at time where you have to stop and think hey why is that, oh yeah, scoping rules and timeout in the call stack, damn!