Language Specification
I mentioned TC39, the technical steering committee that manages JS. Their primary task is managing the official specification for the language. They meet regularly to vote on any agreed changes, which they then submit to ECMA, the standards organization.
JS’s syntax and behavior are defined in the ES specification.
ES2019 happens to be the 10th major numbered specification/revision since JS’s inception in 1995, so in the specification’s official URL as hosted by ECMA, you’ll find “10.0”:
https://www.ecma-international.org/ecma-262/10.0/
The TC39 committee is comprised of between 50 and about 100 different people from a broad section of web-invested companies, such as browser makers (Mozilla, Google, Apple) and device makers (Samsung, etc). All members of the committee are volunteers, though many of them are employees of these companies and so may receive compensation in part for their duties on the committee.
TC39 meets generally about every other month, usually for about three days, to review work done by members since the last meeting, discuss issues, and vote on proposals. Meeting locations rotate among member companies willing to host.
All TC39 proposals progress through a five-stage process—of course, since we’re programmers, it’s 0-based!—Stage 0 through Stage 4. You can read more about the Stage process here: https://tc39.es/process-document/
Stage 0 means roughly, someone on TC39 thinks it’s a worthy idea and plans to champion and work on it. That means lots of ideas that non-TC39 members “propose,” through informal means such as social media or blog posts, are really “pre-stage 0.” You have to get a TC39 member to champion a proposal for it to be considered “Stage 0” officially.
Once a proposal reaches “Stage 4” status, it is eligible to be included in the next yearly revision of the language. It can take anywhere from several months to a few years for a proposal to work its way through these stages.
All proposals are managed in the open, on TC39’s Github repository: https://github.com/tc39/proposals
Anyone, whether on TC39 or not, is welcome to participate in these public discussions and the processes for working on the proposals. However, only TC39 members can attend meetings and vote on the proposals and changes. So in effect, the voice of a TC39 member carries a lot of weight in where JS will go.
Contrary to some established and frustratingly perpetuated myth, there are not multiple versions of JavaScript in the wild. There’s just one JS, the official standard as maintained by TC39 and ECMA.
Back in the early 2000s, when Microsoft maintained a forked and reverse-engineered (and not entirely compatible) version of JS called “JScript,” there were legitimately “multiple versions” of JS. But those days are long gone. It’s outdated and inaccurate to make such claims about JS today.
All major browsers and device makers have committed to keeping their JS implementations compliant with this one central specification. Of course, engines implement features at different times. But it should never be the case that the v8 engine (Chrome’s JS engine) implements a specified feature differently or incompatibly as compared to the SpiderMonkey engine (Mozilla’s JS engine).
That means you can learn one JS, and rely on that same JS everywhere.
The Web Rules Everything About (JS)
While the array of environments that run JS is constantly expanding (from browsers, to servers (Node.js), to robots, to lightbulbs, to…), the one environment that rules JS is the web. In other words, how JS is implemented for web browsers is, in all practicality, the only reality that matters.
For the most part, the JS defined in the specification and the JS that runs in browser-based JS engines is the same. But there are some differences that must be considered.
Sometimes the JS specification will dictate some new or refined behavior, and yet that won’t exactly match with how it works in browser-based JS engines. Such a mismatch is historical: JS engines have had 20+ years of observable behaviors around corner cases of features that have come to be relied on by web content. As such, sometimes the JS engines will refuse to conform to a specification-dictated change because it would break that web content.
In these cases, often TC39 will backtrack and simply choose to conform the specification to the reality of the web. For example, TC39 planned to add a contains(..)
method for Arrays, but it was found that this name conflicted with old JS frameworks still in use on some sites, so they changed the name to a non-conflicting includes(..)
. The same happened with a comedic/tragic JS community crisis dubbed “smooshgate,” where the planned flatten(..)
method was eventually renamed flat(..)
.
But occasionally, TC39 will decide the specification should stick firm on some point even though it is unlikely that browser-based JS engines will ever conform.
The solution? Appendix B, “Additional ECMAScript Features for Web Browsers”.[^specApB] The JS specification includes this appendix to detail out any known mismatches between the official JS specification and the reality of JS on the web. In other words, these are exceptions that are allowed only for web JS; other JS environments must stick to the letter of the law.
Section B.1 and B.2 cover additions to JS (syntax and APIs) that web JS includes, again for historical reasons, but which TC39 does not plan to formally specify in the core of JS. Examples include 0
-prefixed octal literals, the global escape(..)
/ unescape(..)
utilities, String “helpers” like anchor(..)
and blink()
, and the RegExp compile(..)
method.
Section B.3 includes some conflicts where code may run in both web and non-web JS engines, but where the behavior could be observably different, resulting in different outcomes. Most of the listed changes involve situations that are labeled as early errors when code is running in strict mode.
Appendix B gotchas aren’t encountered very often, but it’s still a good idea to avoid these constructs to be future safe. Wherever possible, adhere to the JS specification and don’t rely on behavior that’s only applicable in certain JS engine environments.
Not All (Web) JS…
Is this code a JS program?
alert("Hello, JS!");
Depends on how you look at things. The alert(..)
function shown here is not included in the JS specification, but it is in all web JS environments. Yet, you won’t find it in Appendix B, so what gives?
Various JS environments (like browser JS engines, Node.js, etc.) add APIs into the global scope of your JS programs that give you environment-specific capabilities, like being able to pop an alert-style box in the user’s browser.
In fact, a wide range of JS-looking APIs, like fetch(..)
, getCurrentLocation(..)
, and getUserMedia(..)
, are all web APIs that look like JS. In Node.js, we can access hundreds of API methods from various built-in modules, like fs.write(..)
.
Another common example is console.log(..)
(and all the other console.*
methods!). These are not specified in JS, but because of their universal utility are defined by pretty much every JS environment, according to a roughly agreed consensus.
So alert(..)
and console.log(..)
are not defined by JS. But they look like JS. They are functions and object methods and they obey JS syntax rules. The behaviors behind them are controlled by the environment running the JS engine, but on the surface they definitely have to abide by JS to be able to play in the JS playground.
Most of the cross-browser differences people complain about with “JS is so inconsistent!” claims are actually due to differences in how those environment behaviors work, not in how the JS itself works.
So an alert(..)
call is JS, but alert
itself is really just a guest, not part of the official JS specification.
It’s Not Always JS
Using the console/REPL (Read-Evaluate-Print-Loop) in your browser’s Developer Tools (or Node) feels like a pretty straightforward JS environment at first glance. But it’s not, really.
Developer Tools are… tools for developers. Their primary purpose is to make life easier for developers. They prioritize DX (Developer Experience). It is not a goal of such tools to accurately and purely reflect all nuances of strict-spec JS behavior. As such, there’s many quirks that may act as “gotchas” if you’re treating the console as a pure JS environment.
This convenience is a good thing, by the way! I’m glad Developer Tools make developers’ lives easier! I’m glad we have nice UX charms like auto-complete of variables/properties, etc. I’m just pointing out that we can’t and shouldn’t expect such tools to always adhere strictly to the way JS programs are handled, because that’s not the purpose of these tools.
Since such tools vary in behavior from browser to browser, and since they change (sometimes rather frequently), I’m not going to “hardcode” any of the specific details into this text, thereby ensuring this book text is outdated quickly.
But I’ll just hint at some examples of quirks that have been true at various points in different JS console environments, to reinforce my point about not assuming native JS behavior while using them:
Whether a
var
orfunction
declaration in the top-level “global scope” of the console actually creates a real global variable (and mirroredwindow
property, and vice versa!).What happens with multiple
let
andconst
declarations in the top-level “global scope.”Whether
"use strict";
on one line-entry (pressing<enter>
after) enables strict mode for the rest of that console session, the way it would on the first line of a .js file, as well as whether you can use"use strict";
beyond the “first line” and still get strict mode turned on for that session.How non-strict mode
this
default-binding works for function calls, and whether the “global object” used will contain expected global variables.How hoisting (see Book 2, Scope & Closures) works across multiple line entries.
…several others
The developer console is not trying to pretend to be a JS compiler that handles your entered code exactly the same way the JS engine handles a .js file. It’s trying to make it easy for you to quickly enter a few lines of code and see the results immediately. These are entirely different use cases, and as such, it’s unreasonable to expect one tool to handle both equally.
Don’t trust what behavior you see in a developer console as representing exact to-the-letter JS semantics; for that, read the specification. Instead, think of the console as a “JS-friendly” environment. That’s useful in its own right.