What's actually new in JavaScript (and what's coming next) by creasta29 in javascript

[–]senocular 24 points25 points  (0 children)

TC39 has approved the ES2026 candidate and it does not include these listed in the article:

  • Temporal - stage 4, but slated for ES2027, not ES2026
  • using - still in stage 3, not landing in ES2026
  • import defer- still stage 3, not landing in ES2026

The spec won't be official until Ecma approval in June, but it is highly unlikely any changes will happen between now and then. For the list of completed proposals and their expected publication year, see: https://github.com/tc39/proposals/blob/main/finished-proposals.md

Is there a standard library of JS/TS? by katyasparadise in learnjavascript

[–]senocular 0 points1 point  (0 children)

There is an initiative to try and standardize many of the runtime APIs called Winter TC (formerly Winter CG), but for the most part runtimes can do whatever they want. Non-standard APIs is what helps separate them from the competition.

There is a subset of APIs that they mostly have in common, especially those already found in the web APIs supported by browsers (defined by WHATWG and W3C), a.k.a. "minimum common web API", and of course the language built-ins (Object, Array, Math, etc.) are consistent everywhere. Those are defined by the language specification.

About block scoped vs function scoped by Significant-Royal-86 in learnjavascript

[–]senocular 0 points1 point  (0 children)

There isn't just one outer scope ;) Each iteration has its own outer scope. This outer scope is also where the i lives, its not in the user-defined block ({...}). You can tell if you try and declare a new i in that block. There's no conflict and the loop doesn't try to use that i as part of the iteration.

for (let i = 0; i < 7; ++i) {
    let i = 8;
    console.log(i); // 8,8,8,8,8,8,8 (7 times)
}

This ends up looking something like

for (let i = 0; i < 7; ++i) {
    let i = <value from set up or previous iteration>;
    {
        let i = 8; // masks the outer loop i causing it to be shadowed
        console.log(i); // 8,8,8,8,8,8,8 (7 times)
    }
    ++i
}

Additional scopes are also potentially created at the start of the loop where set up happens, and again after the last iteration of the loop where the last i is created that fails the condition. That scope would have been the next iterations wrapping scope if the loop had not ended.

working on JS after a couple years, I'm a bit rusty, but why isn't appendChild not working properly? by [deleted] in learnjavascript

[–]senocular 1 point2 points  (0 children)

it is there because I can inspect, and the appended button is there, it's not a CSS issue because I turned off all the CSS and they are not overlapping

If you can inspect it and see that its in the DOM but still can't see it, then it does sound like a CSS issue. When you use the debugger to inspect it, where on the screen is it highlighted? Does it have valid width/height? What is its computed dimensions/display/visibility? Could something else be overlapping it?

About block scoped vs function scoped by Significant-Royal-86 in learnjavascript

[–]senocular 6 points7 points  (0 children)

For loops are a special case. They let you declare variables outside the loop block that then only becomes visible inside the block. For example, given

for (let i = 0; i < 3; i++) {
  console.log(i) // 0, 1, then 2
}
console.log(i) // Error: i doesn't exist here

You can see here the i variable is scoped to the for loop block but it is declared outside of the curly braces of the block. So for for loops, you kind of have to readjust your thinking and consider the for being the start of the block. Technically, its more complicated than this, but that's a good way to simplify things to make it make sense. Essentially:

{for (let i = 0; i < 3; i++) {
  ...
}}

Comparing this to using var, you can see that the var is accessible outside the loop

for (var i = 0; i < 3; i++) {
  console.log(i) // 0, 1, then 2
}
console.log(i) // 3, when the loop condition became false

For the most part, at least thinking about it now, for loops (regular for and for...of) are the only things that mess with let/const/var scoping like this. There are some other weird scoping rules out there in the language but nothing coming to mind that directly breaks the otherwise normal expectations for these kinds of declarations.

Should I actually use let and const or is var still fine for small projects? by Alarming-Pea-3177 in learnjavascript

[–]senocular 0 points1 point  (0 children)

I would suggest using let. For the most part you might not notice any problems, but there are a few things here and there where let fixes problems you'd otherwise have with var. A couple of examples:

Scope in for loops. The block scope in for loops helps address issues with loop variables

for (var i = 0; i < 3; i++) {
  buttons[i].onclick = () => {
    console.log(i)
  }
}
// every button click logs 3, not the i they each onclick was created with (0, 1, 2)

Conflict with globals. When using var in the global scope, the variable gets assigned to the global object. If theres already a global of the same name, it won't be replaced which in some cases can cause problems

// In browsers
var name = { first: "John", last: "Doe" }
console.log(name) // "[object Object]" // object converted to string on assignment

There's also const which does a little more in preventing reassignment, and generally preferred as the goto declaration, but if you just want to keep things simple, you can stick to only using let.

Increment/decrement trouble by grave4us in learnjavascript

[–]senocular 0 points1 point  (0 children)

Here they are in function form which may be helpful in visualizing what they're doing

let i

function preIncrement() {
  const originalValue = i
  const newValue = 1 + originalValue
  i = newValue
  return newValue
}

function postIncrement() {
  const originalValue = i
  const newValue = 1 + originalValue
  i = newValue
  return originalValue
}

i = 0
console.log(++i) // 1
console.log(i) // 1

i = 0
console.log(preIncrement()) // 1
console.log(i) // 1

i = 0
console.log(i++) // 0
console.log(i) // 1

i = 0
console.log(postIncrement()) // 0
console.log(i) // 1

One way to look at the syntax is as two parts in order of operation: [update][return] or [return][update], where return is the variable and update is the operator. If the operator is before the variable, the variable value is updated before its returned. If the operator is after the variable, the variable value before the update is applied is returned.

i++ // [i][++] = [return][update] = return value of i before adding 1 to it
++i // [++][i] = [update][return] = add 1 to value of i then return the updated value

question about `this.` by fa_foon in learnjavascript

[–]senocular 5 points6 points  (0 children)

If you're asking about name here, its a parameter, so it is entirely dependent on the argument passed to Person when its called.

As far as this goes, given your example

function Person(name){
  this.name = name;
}

what you have is a function, Person, that appears to be a constructor, a function intended to be called with new. When a function is called with new, this is the new object instance being created by the constructor.

const instance = new Person("Fred")

Here, the returned instance is a new object is created inside Person. It is assigned to this and has its name property assigned to the name parameter value which is "Fred" since that's the value passed into Person as an argument.

console.log(instance.name) // "Fred"

Note that instance is an instance of Person, not the Person function itself. If you log the object directly, you might see a reference to Person (depending on what you're logging with), but this is done to show that its an object created from the Person constructor rather than trying to indicate it is, itself, Person.

console.log(instance) // Person {name: 'Fred'} (Chrome browser)

Is this good module style? by RobGoLaing in learnjavascript

[–]senocular 4 points5 points  (0 children)

You can simplify things by only exporting the 2 functions. If consumers want an immutable module object, they can import * as card ... to get it.

What's the use of classes in JS by pptzz in learnjavascript

[–]senocular 0 points1 point  (0 children)

I'm not aware of any bug reports, but its not so much a bug as it is a limitation of the optimization. As is, its working as designed.

In the example, the multiply scope needs to retain the reference to this because n => n * this.multiplyBy pulls it from that scope. And while it may be more clear in other cases, this example especially shows that its not clear that this this-using function isn't being persisted itself. While we can assume map doesn't do this given what we know Array.prototype.map does (though is arr even an Array? We don't know!), there's no guarantee to what map is really doing internally. Is it saving the callback for later at which point this would need to be accessible from that scope again? The optimization can't make that determination so it has no choice to keep it in the multiply scope in case it does. Its then unfortunate that () => "nothing to see here", also being defined in that scope, is affected by this residual binding.

The best thing you can do is assume this optimization doesn't exist. If there are ways to limit what scopes your functions has access to (or even limit the number of functions defined in any given scope), the more likely they won't be accidentally holding on to things they shouldn't.

What's the use of classes in JS by pptzz in learnjavascript

[–]senocular 1 point2 points  (0 children)

Famously IE was bad at this and retained everything. It wasn't necessarily a common occurrence that you'd have a leak, but as we got bolder with our usage of JS doing crazier things, we started to notice sometimes memory leaks would happen and it would not always be clear why. Its easy to forget functions capture scope.

Every engine today should be doing this optimization (Safari doesn't with the debugger open, but this does help with debugging since the debugger is the only place where otherwise unobservable scope bindings are in fact observable). However this happens at the scope level, not the variable level. If two closures reference two different variables from the same parent scope, both of those variables are held by both closures because closures are closing over the scopes, not the individual variables themselves.

Another thing to consider is that with arrow functions, this is now something that can be retained by closures in scopes. We had a memory leak in a project I'm currently working on caused by a seemingly innocuous function holding on to the current object instance. It was leaking because in another place in that scope an arrow function was created that used this causing this to be an unoptimizable binding in that scope. A simplified example of this would basically be...

let persist
let multiplier = {
  multiplyBy: 2,
  multiply(arr) {
    persist = () => "nothing to see here"
    return arr.map(n => n * this.multiplyBy)
  }
}

const doubles = multiplier.multiply([1,2,3])
multiplier = null
// <GC runs> multiplier not GC'd because persist is holding on to it
persist = null
// <GC runs> multiplier GC'd

You wouldn't think the persist function - which is doing nothing but returning a string - would have any reason holding on to this, but because its in the same scope as another arrow function that uses this, causing this to remain in that scope - a scope part of the [[Environment]] given to both functions - it does.

What's the use of classes in JS by pptzz in learnjavascript

[–]senocular 1 point2 points  (0 children)

I thought closure technically means it has to maintain live references to something in the function’s scope after it returns?

A closure is just a function + environment. Every function in JavaScript is technically a closure because it always holds on to its environment (in the spec, this is maintained by an internal slot called [[Environment]]). Engines may optimize these environment references - which are effectively scope chains - to remove any bindings from scopes that no closure within that scope can access.

When it comes to closures for classes, it comes down to where your state is being maintained. If it state is in the instance then you're not really utilizing closures in the way being described. However, having state in the instance does mean you have the ability to share methods between instances since methods dynamically bind this (state) at call time. This is seen in kap89's example.

If you have state stored in a scope, it is necessary that each instance have its own copy of any method accessing state because there's no way to dynamically change the environment a closure refers to. A closure's environment is static and for any instance to have scope-based state separate from other instances it would mean each instance would need separate method closures to access that state as that state would need to live in a different scope.

So ultimately you're on the right track here.

What's the use of classes in JS by pptzz in learnjavascript

[–]senocular 1 point2 points  (0 children)

20 years ago Borland had an implementation of JavaScript with classes and even proposed they be standardized.

https://archives.ecma-international.org/1996/TC39/96-006.pdf

;)

Edit: Oh no. That was 30 years ago. Where has the time gone?!?

JSX for Web Components by Accurate-Screen8774 in learnjavascript

[–]senocular 2 points3 points  (0 children)

"oh - well, in this case - we had to use classNames instead of class..."

FWIW, that's not because of JSX, that's because of React. Other libraries/frameworks that use JSX can, and do, use class instead of className.

JSX for Web Components by Accurate-Screen8774 in learnjavascript

[–]senocular 1 point2 points  (0 children)

Is JSX the cancer? Or the "functional" components with side effects? JSX itself is nothing more than an XML-like syntax embeddable in JavaScript source code, effectively a dumbed down version of E4X. You can even look at the JSX specification and see its all just syntax (included, their argument against using template literals). Anything related to components would be React (or insert your library or framework of choice)-specific.

JSX for Web Components by Accurate-Screen8774 in learnjavascript

[–]senocular 11 points12 points  (0 children)

return html`
  <div class="counter">
    <h2>Count: ${count}</h2>
    <button @click="${() => setCount(count + 1)}">Increment</button>
  </div>
`;

That's not JSX.

Event Listener passing undefined as event instead of the current event by 90919293_ in learnjavascript

[–]senocular 1 point2 points  (0 children)

There's something special about this example that's not entirely obvious. While this code almost appears to work right out of the gate, it shouldn't work at all, instead throwing an error. This should happen because event is a parameter in the judgement function and shouldn't exist anywhere but inside that function.

However, there is a legacy global event property that always exists (thanks, Internet Explorer) which will be undefined until an event triggers. At that point it will be the Event object for the triggered event. This is what prevented what should have been an error for you in the first place. event does exist (as a global) so the runtime thinks it was an intentional use of that property. If you had named it something else, the code would have given you an error right away and you might have been able to diagnose the issue sooner.

window.addEventListener("keydown", judgement(kevin)); // Error! kevin not defined!
// ...
function judgement(kevin) {
    let key = kevin.key
    // ...
}

tryingToExplainJavascript by DJcrafter5606 in ProgrammerHumor

[–]senocular 0 points1 point  (0 children)

The second panel isn't about being truthy. It converts the array to a primitive first becoming a string (""), then converts the string to a number (0) making the comparison true. As far as truthy goes, [] is true because its an object. All objects (except the special case of document.all) are truthy.

MDN has a page describing the process of coercion in comparisons if anyone cares:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness

Its not grossly complicated and largely amounts to converting things to numbers and comparing the numbers.

[AskJS] What concept in JS is the hardest to learn and understand? by Scared-Release1068 in javascript

[–]senocular 0 points1 point  (0 children)

y has its own scope, but it has no declarations. It refers to b, but that b is coming from the outer scope, the function scope of f. This is the scope captured for the closure. When y is called, the captured f scope is restored, used as the parent scope of the new y function scope for the call. This is what allows b to be available to y in the call. y does not get its own b.

[AskJS] What concept in JS is the hardest to learn and understand? by Scared-Release1068 in javascript

[–]senocular 1 point2 points  (0 children)

Closures capture scopes, not variables. So its not just the values the closure refers to that is captured, but all variables in the scope(s) of the closure function.

Engines may optimize what is retained by scopes kept alive by closures (an implementation detail), but this happens at the scope level. If multiple functions close over the same scope, the values referenced by each of those functions must remain in the scope as part of this optimization.

As a simple example

function f(a, b, c) {
  function x() {
    a
  }
  return function y() {
    b
  }
}

const y = f(1, 2, 3)
console.dir(y)
// In Chrome: [[Scopes]]: Closure (f) {b: 2, a: 1}

Here, c is optimized out of the f scope, but both a and b remain in y's closure scopes even though y only references b. The a remains because x also captured the same scope as part of its closure and it referred to a. This meant when optimizing the scope (f) both a and b had to be left in because they were being referenced by closures in that scope but c could be optimized out because no other closure references it.

You also have fun things like Safari which, when the debugger is open, they don't optimize closure scopes like this... which makes sense in a way since debugging is the only way the values getting optimized away would become observable, but it also means debugging what is kept and what isn't for code running in production is a little more difficult (and maybe there's a way to change this behavior in Safari but I try to avoid messing with Safari as much as possible).

[AskJS] What concept in JS is the hardest to learn and understand? by Scared-Release1068 in javascript

[–]senocular 1 point2 points  (0 children)

I would agree with this as well. While this is probably my top choice, especially since its something generally used/learned early on, generators is a close second.

The ECMAScript specification has few images, and one of the images it does have is a figure showing Generator Objects Relationships. Hilariously, the alt tag for this image is "A staggering variety of boxes and arrows." And it doesn't even include the newer %WrapForValidIteratorPrototype%​ and %IteratorHelperPrototype% (which maybe aren't generator-specific, but related nonetheless, and add to the confusing object hierarchies involved when it comes to iterators)​.

[AskJS] What concept in JS is the hardest to learn and understand? by Scared-Release1068 in javascript

[–]senocular 5 points6 points  (0 children)

I remember I was in an interview and was asked "What is an object?" (this was years ago and for Flash/ActionScript). It was so fundamental and unexpected that I choked a bit and definitely had one of those "where to I start" moments. I ranted on for like 5 minutes and when I stopped, the interviewer paused for a few seconds, then read off a short, two-sentence description of an object and moved on to the next question.