The Many Flavors of JavaScript
The popularity of JavaScript has risen sharply since 2000, transforming developer culture and creating opportunities to use the language in ways the world has never seen before. Unfortunately, the rapid rise in JavaScript’s popularity has also created a number of knowledge gaps.
Some of the most significant gaps are the hardest to see. Many different technologies blend together with JavaScript so well that it’s tough to tell where JavaScript ends and the other technology begins. I call this the “peanut butter” effect, because everything seems to go well with peanut butter.
The truth is, there are many different flavors of what the world calls “JavaScript”. The following breakdown is designed to differentiate each of the “flavors” from each other.
What is JavaScript?
First and foremost, JavaScript is an interpreted programming language. In simplistic terms, it requires an engine/runtime to actually do anything.
Some people argue JavaScript isn’t interpreted. Some JavaScript engines interpret code until the engine recognizes that compilation would help performance. It will compile portions of the code to achieve better performance. It is an implementation detail of the engine/runtime, not the language. It all happens behind the scenes.
What is a JavaScript engine?
A JavaScript engine is responsible for executing the code. While the details of these engines are beyond the scope of this document, it is important to understand they are not all the same.
The differences are most apparent in popular engines like V8 (Chrome/ Node.js), Spidermonkey (Firefox), and Chakra (Microsoft Edge). There are several others as well, but these major engines implement features at a different pace. Many frontend developers frequently visit sites like caniuse.com to determine whether a specific JavaScript feature is available yet in the engines they support.
UPDATE: Microsoft Edge is now based on V8
UPDATE 2: Microsoft Edge is now based on Chromium (and V8). Chakra appears to have fallen to the wayside.
A super-abbreviated and often overlooked history:
The language, originally known as Mocha/LiveScript, became “JavaScript” in 1995 when it was released in Netscape Navigator 2 (browser). It was intentionally designed to provide developers with an interpreted/live scripting capability that could interact with a compiled language (Java).
Ultimately, these events lead to the creation of the TC39 governing body, which was tasked with creating a standard language specification.
ECMAScript: Raw JavaScript
ECMAScript is a language specification. It is basically the blueprint of how JavaScript should work. Anyone who wants to create a standard JavaScript engine uses this specification to parse and execute JavaScript code in a predictable manner.
The specification is always changing. New features, modifications, and deprecations are submitted as proposals to TC39. Once approved, they become part of the specification. However; this does not necessarily mean such changes are immediately accessible to developers.
Specifications must be implemented in a JavaScript engine before they become usable. While this sounds logical, it sometimes creates a false presumption that a feature will actually be adopted. Just because the blueprint is drawn a certain way doesn’t mean it must be built that way. Most JavaScript engine authors prefer to follow the specification, but they don’t necessarily follow it in the same order or release features at the same time as other authors.
Some vendors implement draft versions of a specification before a proposal is approved and officially part of the specification. Historically, this has been done by industry heavyweights, such as Microsoft or Google. It’s often done benignly, usually as a means of gauging developer interest in a particular language capability. However; there have been occasions where vendors submit competing proposals to TC39 and implement changes in support of their own agenda. While this doesn’t occur often anymore, it highlights the fact that JavaScript can vary from engine to engine and is influenced by opinion.
Transpiled Languages (JavaScript Lookalikes)
Transpiled languages like CoffeeScript and TypeScript look similar to JavaScript, but aren’t actually JavaScript. Others, like Emscripten, convert entirely different languages into JavaScript.
Since JavaScript is an interpreted language, engines process instructions (code) on-the-fly. Transpiled languages must first be converted into JavaScript before they can be interpreted.
Transpilation is the process of converting one language to another. It gets its name from the combination of the terms “transform” and “compile”. This is a little misleading because compilation does not actually occur in transpiled languages. The “transpilation” namesake carries over from the world of compiled languages like C/Java/Go, where a build process converts source-to-binary code that can run on a computer. Transpilation is source-to-source conversion.
Transpiled languages are often created to solve pain points that occur in an interpreted language like JavaScript. For example, TypeScript provides conventions for strong typing. JavaScript is a weakly-typed language, which some development teams struggle with. TypeScript fills this gap by enforcing different practices and polyfilling concepts. Other languages, like CoffeeScript, are designed to simplify the syntax of JavaScript.
A slight editorial: Transpiled languages often solve real and perceived language problems, but ironically create a standardization problem (the very problem many of them seek to resolve). Sharing code between developers requires other developers to understand both the transpiled language and JavaScript (Two languages instead of one). However; transpiled languages serve a great purpose by informing TC39 of public opinion and pain points. Several features from transpiled languages have landed in the ECMAScript specification.
A note about the Babel Transpiler:
Babel is a popular transpiler that converts JavaScript to JavaScript. WHAT?! Remember, not all JavaScript engines are created equally.
The ECMAScript specification underwent many changes since 1995, and it continues to change at an increasingly rapid pace. ECMAScript 2015 (ES6) defined a substantial number of new features and changes to the language. These were exceptionally well-received by the development community, but created a large backlog of work for JavaScript engine authors.
By this point in time, many browsers had gone “evergreen” (self-updating). This created a new global environment of continually providing end users with newer JavaScript engines (and therefore new ECMAScript features). Microsoft introduced Edge in 2016, an evergreen browser, which became the standard for Windows 8.1/10 users, but it won’t run on older versions of Windows. Over 49% of Windows users still run Windows 7 or below. While these users can install Chrome/Firefox, the default browser on these systems is still Microsoft Internet Explorer 11 (or prior). IE11 is not evergreen, and it only supports the older ECMAScript 5 (ES5) specification. It will never support newer versions.
The “IE11 problem” highlights a current problem for the JavaScript community. How does one support two different versions of ECMAScript? Simply put, you can’t.
Developers want to use the exciting new features of ES6, but this would exclude a sizable number of users from being able to use their web pages. Continuing to use ES5 forces developers to continue dealing with problems the language has already resolved, leaving development in a stagnant state.
Babel attempts to solve this problem by transpiling modern JavaScript to older JavaScript, allowing developers to write code once and still run it on older browsers like IE11. In other words, most developers still release ES5 code that works on all browsers, even though they’re only maintaining ES6 code.
JavaScript API’s
One of the most common misconceptions about JavaScript is that it is anything more than a language. The language is consistent, but the runtimes/engines provide very different API’s.
For example, Node.js provides a JavaScript API for interacting with the file system and other features of the operating system. This is very different from the JavaScript API provided by browsers, which primarily focus on DOM interaction and data management.
Think about REST/HTTP API’s like Google Maps. Even though it’s not written in JS, it’s accessible using JS. So is Node’s File System API; Same language, different API’s.
Node.js
Node.js is an incredibly popular runtime. It is used by over 8 million people. However; it is very important to understand that Node.js is not JavaScript. It merely interprets/runs it.
Furthermore, Node.js is not a server side language. The runtime provides a JavaScript API for interacting with the file system/operating system (see the Node Docs). Node doesn’t care if the file system/OS is on a server, desktop, or IoT device.
When people “write Node.js code”, it really means JavaScript code with a “file system/OS flavor”.
Deno
Deno is a new runtime from Ryan Dahl (who created Node.js). He describes it as an evolution. Paraphrasing his words, Node was great for the 2010’s, but JavaScript has changed and Deno is his solution for the next decade. Similar to Node, it uses V8 to process JavaScript. It solves the same kinds of problems Node does, but in a slightly different way.
The introduction of Deno in 2020 really highlights the fact the world now has many different JavaScript runtimes. Deno is not a new language. It is a new runtime.
Browsers
A little-known fact is JavaScript was introduced as a browser feature in 1995, but also as a server-side language in 1996 with Netscape Enterprise Server. The server-side implementation wasn’t well-adopted by developers, so JavaScript grew up in the browser.
In simple terms, browsers consist of a JavaScript engine and a rendering engine. The rendering engine is responsible for HTML and CSS, while the JavaScript engine provides functionality.
In browsers, the JavaScript API provides an interface for interacting with HTML, i.e. the Document Object Model (DOM) and CSS styles. These are both concepts that do not exist in Node.js, so “Node code” looks very different from “browser code”.
When someone is asked if they know JavaScript, it’s usually in reference to “browser-flavored” JavaScript API’s.
JavaScript Libraries
In my opinion, the JavaScript language is not that hard to learn. However; many of the aforementioned API’s can feel complex, tedious, or downright esoteric.
Libraries are created to simplify complex or confusing API’s. They lower the mental barrier of using a particular API.
Using a library is not the same as using the JavaScript language. However; using a library does not imply “JavaScript beginner”. Even the most experienced JavaScript developers use libraries. Instead of wasting brain power on boilerplate tasks, that brainpower can be spent on the important parts of building applications and systems.
Libraries are utilitarian, meaning they only simplify fundamental problems. For example, jQuery was (and still is) a popular JavaScript library. It is most notable for simplifying the process of issuing network requests, using the popular $.ajax({...})
method. For many developers, this is much more intuitive than constructing an XHR request by hand.
Again, libraries simplify JavaScript API’s. Let’s be very clear: jQuery is not JavaScript. No library is JavaScript.
JavaScript Libraries are not frameworks. Frameworks solve a different type of problem.
Libraries are a little bit like a buffet. All of the ingredients are expertly-prepared and laid out in a spread. It’s up to the developer to pick and choose what they want to use. As a result, each application takes on a flavor of it’s own, based on the unique choices each developer makes.
JavaScript Frameworks
Hardly a day goes by in developer-land without hearing someone talk about Vue, React/Redux, or Angular. These are JavaScript browser frameworks. JavaScript frameworks are not JavaScript.
Frameworks are similar to combo meals at a restaurant. They provide common combinations of libraries, API’s, and raw JavaScript in “ready-to-consume” packages. Of course, the “best combinations” are an opinionated selection created by the framework authors.
Opinions, like personal tastes, differentiate frameworks. They typically consist of several micro-libraries and/or their own API. Some follow a “convention over configuration” development approach while others follow the exact opposite. Some frameworks value composition development patterns while some value inheritance. Others value functional programming and other approaches.
There are tons of patterns and “ways” to write applications. When someone asks if you know React, they’re not just asking if you know JavaScript. What they’re really asking is a series of questions:
- Do you understand the values of the ____ framework?
- Can you orchestrate an application using the ____ framework libraries?
- Do you understand the ____ framework user community?
Browser frameworks are highly opinionated, but they offer some level of standardization for free. Development teams often align themselves with a framework because they create consistency (when done properly). This makes it easy to pass work from one developer to another without a huge transition process.
Of course, non-browser frameworks exist as well.
Web Assembly (WASM)
There has been alot of talk about web assembly amongst the global developer community. WASM is not JavaScript.
WASM is a compilation target for other languages to create JavaScript bindings. In simpler language, it’s possible to add-on to the JavaScript engine/runtime, using other programming languages like Rust, Go, or C. Developers cannot change the JavaScript language itself, but they can create new JavaScript API’s with WASM.
For example, the JavaScript language does not have method called encodeMyVideo()
. With WASM, it’s possible to create a video encoder using another language and make it available to the JavaScript runtime with a binding called encodeMyVideo()
.
WASM is the evolution of browser plugins. Browsers used to have Java plugins, Flash plugins, ActiveX, and many other “browser add-ons”. Pretty much all of those failed over time, but WASM uses a different approach. Instead of being a plugin runtime, it is a specification.
Hybrids: Desktop JavaScript API’s
JavaScript continues to be the language of choice for many new types of API’s, including desktop applications. Desktop development has seen a huge surge in activity since the introduction of NW.js (formerly node-webkit) and Electron. Even Qt has a JS-like runtime engine.
This is important because it is yet another discipline JavaScript has touched in a significant way. However; knowing JavaScript does not mean someone knows how to develop a desktop application (see a trend here yet?). It just means it is possible and people do it. The JavaScript code for a desktop application may look similar to code for a web app and a Node app combined, or it could be very different.
For a more in-depth look at this particular flavor of JavaScript, here’s a talk I gave on Electron for Bleeding Edge Web.
Obscure JavaScript
JavaScript has found its way into even the most obscure places. Take NGINScript, for example, which is a JavaScript runtime for manipulating HTTP requests in NGINX. Network/traffic management is not something many developers associated with JavaScript until recently.
Other tools like otto exist, which is a JavaScript engine written entirely in Go. This highlights how JavaScript has even been sprinkled into other modern programming languages.
Do you Know JavaScript?
By this point, it should be clear JavaScript is:
- A language used across many disciplines.
- Used for a multitude of purposes.
- Has many different API’s.
Don’t be fooled into thinking knowledge of JavaScript creates an instant knowledge of JavaScript API’s. A developer who knows the JavaScript language doesn’t necessarily know Node API’s, DOM API’s, libraries, or frameworks.
Since there are so many different API’s, JavaScript code can look very different. The language is the same, but the context in which JavaScript is used creates many flavors.