TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

On JavaScript's Weirdness

95 点作者 n2d4大约 1 个月前

22 条评论

pcthrowaway大约 1 个月前
Many 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.<p>In the first example:<p><pre><code> for (let i = 0; i &lt; 3; i++) { setTimeout(() =&gt; console.log(i)); } &#x2F;&#x2F; prints &quot;0 1 2&quot; — as expected let i = 0; for (i = 0; i &lt; 3; i++) { setTimeout(() =&gt; console.log(i)); } &#x2F;&#x2F; prints &quot;3 3 3&quot; — what? </code></pre> i&#x27;s scope is <i>outside</i> the for loop in the second example, <i>and</i> the setTimeouts execute in the call stack (e.g. the next run of the event loop), after i has finished incrementing in the first event loop iteration<p>Consider that you&#x27;d have the same issue with the older `var` keyword which is lexically scoped<p><pre><code> for (var i = 0; i &lt; 3; i++) { setTimeout(() =&gt; console.log(i)); } &#x2F;&#x2F; prints &quot;3 3 3&quot; because i is not block-scoped </code></pre> If for some reason you really need some work run in the next call stack, <i>and</i> you need to use the value of a variable which is scoped outside the loop and modified inside the loop, you can also define a function (or use an iife) to pass the value of i in the current iteration into (rather than getting the reference of i in the event loop&#x27;s <i>next</i> call stack)<p><pre><code> let i = 0; for (i = 0; i &lt; 3; i++) { ( (x)=&gt;setTimeout(()=&gt;console.log(x)) )(i) } &#x2F;&#x2F; prints 1 2 3</code></pre>
评论 #43581894 未加载
评论 #43584897 未加载
评论 #43584328 未加载
评论 #43584164 未加载
评论 #43581921 未加载
glenjamin大约 1 个月前
Complaining about the for loop behaviour seems odd. Variables declared in the expression section of the loop are scoped to the loop body - this is generally reasonable and the least likely to produce errors.<p>Notably, Go initially decided to stick with the C-style approach and have the scope be outside the loop, and has since decided that the tiny performance improvement this provides isn&#x27;t worth the many many times it&#x27;s tripped people up, and has changed the semantics in recent versions: <a href="https:&#x2F;&#x2F;go.dev&#x2F;blog&#x2F;loopvar-preview" rel="nofollow">https:&#x2F;&#x2F;go.dev&#x2F;blog&#x2F;loopvar-preview</a>
评论 #43580895 未加载
brap大约 1 个月前
I remember properly learning JS from “The Good Parts” book, which makes it known from the start that JS is a nasty language but if you ignore many sharp edges it can be nice and elegant. I think this is especially true with (a subset of) TS. All you need is a very, very strict linter, and then you get to pretend you’re working in a mostly solid language.
评论 #43584940 未加载
nobleach大约 1 个月前
I find these oddities far more realistic than the Wat video from a long time ago. Many of the things in that video had me asking, &quot;sure but...what programmer would blindly try these things and then be shocked when they didn&#x27;t work?&quot; The examples in this article are actual &quot;gotchas&quot; that could silently bite someone.
评论 #43582346 未加载
Leszek大约 1 个月前
The eval thing is listed as a potential performance cost, but it&#x27;s actually super important for performance, because it allows the parser to statically know that sloppy eval is never called inside a function, and that variables can therefore be optimized away.
vanderZwan大约 1 个月前
The list of &quot;other weirdness&quot; at the end mentions:<p>&gt; <i>+0 vs. -0</i><p>Which feels kind of odd, since that&#x27;s mostly floating point weirdness, not JS weirdness. Unless they mean the fact that you <i>can</i> force V8 to initialize a zero-value as a double using -0 as a literal (it tends to optimize 0 to integers). But that has no effect on real-world code unless you use Math.sign, or divide by minus zero.
评论 #43582470 未加载
smjburton大约 1 个月前
While interesting and possibly helpful to new coders, are these quirks of the language still relevant when most development in Javascript is done using a framework (React, Vue, etc) these days? How often do these &quot;gotchas&quot; factor into &quot;modern&quot; Javascript development, especially in production? These type of articles seem to critique mechanics of the language that don&#x27;t come up as often in practice.
评论 #43582484 未加载
评论 #43583995 未加载
评论 #43583373 未加载
评论 #43583194 未加载
machine_ghost大约 1 个月前
What I don&#x27;t understand is why, after twenty years, we still haven&#x27;t versioned Javascript. A simple:<p>&#x27;v2&#x27;;<p>At the top of every file could let us eliminate all this 20-year old cruft (like document.all hacks to support Internet Explorer).<p>Yet, despite the already established `use strict`; (which is basically &#x27;v1.5&#x27;), the community seems completely against modernizing the language.
评论 #43585269 未加载
blatantly大约 1 个月前
I&#x27;d forgive a few of those. Unicode is Unicode. The for loop capture behaviour makes sense to me. Missing semis should also be in your linter. Sparse arrays is the sort of feature you&#x27;d read up on if you use and not rely on intuition. It makes sense that if you loop over a sparse thing the looping is sparse too.
评论 #43580799 未加载
jmull大约 1 个月前
Variable declared outside the loop construct lives outside the loop.<p>Variable declared inside the loop construct lives inside the loop.<p>Seems intuitive to me.<p>The complex thing here (and what seems to have confused the author) is the distinction between when the reference to the variable is captured vs. when the setTimeout() callback occurs.<p>Actually, this article shows what good shape Javascript is in. As it says, commonly deployed linters catch the really bad stuff, effectively deprecating those things.<p>Pretty much the rest of it to do with specific domains outside of javascript, like what actually is a character and IEEE floating point, or are rather out-of-the-way things like document.all and sparse arrays (not that people don&#x27;t use sparse arrays, but it&#x27;s entirely optional and if you&#x27;re going to voluntarily go into that cave, I guess you must be happy to tangle with the bears living there.)
theThree大约 1 个月前
<p><pre><code> function f1(a:number, b:number, c:number, d:number) { [a, b] = [b, a] [c, d] = [d, c] console.log(a, b, c, d) } </code></pre> For the above codes Typescript gives error message: Type &#x27;number[]&#x27; is not assignable to type &#x27;number&#x27;.
skrebbel大约 1 个月前
I&#x27;d like to understand why `document.all` is slower than `getElementById`. Couldn&#x27;t any even somewhat decent optimizing compiler trivially compile the first to the latter? Like, I don&#x27;t mean in weird cases like `const all = document.all; return all[v]`, or iterating over it, just the general one where someone directly does `document.all.foo` or `document.all[v]`, ie the 99.99% case. When faced with the choice to compile those accesses to getElementById calls, or patch the ecmascript standard to put IE-compat workarounds in there, it seems kinda nuts to me that they would choose the latter, so I bet there&#x27;a good reason that I&#x27;m missing.
评论 #43582027 未加载
评论 #43582696 未加载
评论 #43585116 未加载
0xbadcafebee大约 1 个月前
I don&#x27;t mind &#x27;0&#x27; == 0 when it&#x27;s used for scripts and dumb stuff. That&#x27;s literally how shellscript works, and I love shellscript, so I can&#x27;t complain about that.<p>But I would never use shellscript to build an entire business&#x27;s user interface with a giant shellscript framework. That would be insane. A language that was designed as a throwaway scripting thing for doing some miscellaneous tasks, and never designed for full application purposes? No sane person would use that for a business&#x27;s core product.<p>Right?
Klaster_1大约 1 个月前
These are the well known bad parts every JS learner is taught about from numerous sources. I guess its OK to repeat the basics, but the linked article looks more like a blog spam rehash.
chuckadams大约 1 个月前
I&#x27;ve a few opinions on the content, but I&#x27;m most interested in which unicode analyzer tool generated that nifty text tree diagram.<p>I can deal with JS&#x27;s warts because the tooling is so much better. The other language I make my living with is PHP, and while it&#x27;s much improved from before, the global function namespace is still a garbage fire, and you need extra linters like phpstan-safe-rule to force you to use sane wrappers.
评论 #43580827 未加载
评论 #43582539 未加载
larrik大约 1 个月前
<p><pre><code> “JavaScript sucks because &#x27;0&#x27; == 0!” - literally everyone ever </code></pre> I never really understood the hate for this, given that everything is a string in HTTP, and that SQL does the same damn thing. There are far more annoying things about JS (both the language and the ecosystem).
评论 #43581525 未加载
评论 #43582508 未加载
edwinjm大约 1 个月前
These weird cases are not because JavaScript is bad, but because it has to be backwards compatible. So where other languages can just delete old quirks from the language, JavaScript has to keep them in.
kazinator大约 1 个月前
&gt; <i>In any programming language, when you capture values with a lambda&#x2F;arrow function</i><p>It seems like just a few years ago that few programmers knew what these concepts are: mostly just the few that were exposed to Lisp or Scheme in college.<p>Now it&#x27;s in &quot;any language&quot; and we have to be exposed to incorrect mansplaining about it from a C++ point of view.<p>&gt; <i>there are two ways to pass variables: By value (copy) or by reference (passing a pointer). Some languages, like C++, let you pick:</i><p>Lexical capture isn&#x27;t &quot;pass&quot;.<p>What this person doesn&#x27;t understand is that C++ lambdas are fake lambdas, which do not implement environment capture. C++ lambdas will not furnish you with a correct understanding of lambdas.<p>(Furthermore, no language should ever imitate what C++ has done to lambdas.)<p>Capture isn&#x27;t the passage of arguments to parameters, which can be call by value or reference, etc. Capture is a retention of the <i>environment</i> of bindings, itself.<p>The issue here is simply that<p>1. The Javascript lambda is correctly implementing lexical capture.<p>2. The Javascript loop is not creating a fresh binding for the loop variable in each iteration. It binds one variable, and mutates its value.<p>Mutating the value is the correct thing to do for a loop construct which lets the program separately express initialization, guard testing and increment. The semantics of such a loop requires that the next iteration&#x27;s guard have access to the previous iteration&#x27;s value. We can still have hacks under the hood so that a lexical closure will capture a different variable in each iteration, but it&#x27;s not worth it, and the program can do that itself. Javascript is doing the right thing here, and it cannot just be fixed. In any case, vast numbers of programs depend on the variable i being a single instance that is created and initialized once and then survives from one iteration to the next.<p>Now lambdas can in fact be implemented by copy. Compilers for languages with lambda can take various strategies for representing the environment and how it is handled under capture. One possible mechanism is conversion to a flattened environment vector, whereby every new lambda gets a new copy of such a vector.<p>The entire nested lexical scope group becomes one object in which every variable has a fixed offset that the compiled code can refer to. You then have to treat individual variables in that flat environment according to whether any given variable is shared, mutated or both.<p>The worst case is when multiple closures capture the same variable (it is shared) and the variable is mutated such that one closure changes it and another one must see the change. This is the situation with the loop index i variable in the JS loop. This means that under a flat, copied environment strategy, the variable will have to be implemented as a reference cell in the environment vector. Variables which are not mutated can just be values in the vector. Variables which are mutated, but not shared among closures, likewise.<p>This is all under the hood though; there are no programmer-visible annotations for indicating how to treat each captured variable. It always looks as if the entire environment at the point of capture is being taken by reference. The compiler generates reference semantics for those variables which need it.<p>At the implementation level, with particular strategies for handling environments under lambda, we can think about capturing references or value copies. C++ lambdas imitate this sort of implementation-level thinking and define the language construct around it, in a way that avoids the master concept of capturing the environment.
ramesh31大约 1 个月前
My favorite one ever:<p>017 == &#x27;17&#x27; &#x2F;&#x2F; false<p>018 == &#x27;18&#x27; &#x2F;&#x2F; true
coolThingsFirst大约 1 个月前
Js trivia, no need to know this stuff.
RKFADU_UOFCCLEL大约 1 个月前
&gt; but every JS setup these days contains a linter that yells at you for code like that.<p>Yes, it can find <i>some</i> cases, but in general would require solving the halting problem.
rs186大约 1 个月前
I 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 doesn&#x27;t take more than a few minutes to figure out. And if you write in TypeScript, like almost every serious new project created these days, most of these questions don&#x27;t exist at all.<p>Which is why I don&#x27;t bother reading these posts any more.
评论 #43581985 未加载
评论 #43583198 未加载
评论 #43582181 未加载
评论 #43583856 未加载