It’s a great wonder how JavaScript hasn’t successfully broken the Internet — not really. There’s no debate that horror stories still floats around in the great dark web of shoddy coding, but in the general grand scheme of things, updates to the language haven’t caused that many major issues.
Well, most of the issues are possibly bought on by Internet Explorer — but that’s a completely separate matter. Explorer has always been the ‘extra’ one when it comes to supporting JavaScript.
So the question is, how has JavaScript manage to keep everything so stable, regardless of releases over the past 20-ish years? Despite all the side looks and jeering remarks from other more ‘proper’ programming languages, how is it that a technology released back in 1995 still manages to hold up against time?
The Fine Art of Versioning
The idea behind versioning is that with every major release, outdated features are either updated or removed from the language, framework or library. This means that new code won’t work in older implementations.
Code written from version 2 will never work for something that’s running on version 1. Versioning is often the approach when the language does a major upgrade. For many, it is an ‘all or nothing’ kind of approach, with the code instantly breaking if something from a different version makes an appearance.
The alternative to this approach is a tagging system where you can allow for multiple versions to exist on a single code base. With JavaScript, you can manually declare through Content-Type
For example, the following declaration will tell the program to apply a specific version of ECMAScript.
Content-Type: application/ecmascript;version=6
While this is not a required necessity in many cases, using the Content-Type
declaration can come in handy for things like Explorer. So we’re talking about from the beginning of time JavaScript up to version 6.
The alternative to this is the in-band approach which declares which version to specifically apply to the code. To do this, you can use use
by starting a file with the following line:
use version 6;
The issue with this approach is every time you declare a different version, the code base is forked and the engine needs to maintain each version in parallel. As a result, engines can become bloated as it tries to implement the different semantics and keep it all running in parallel. Code also becomes harder to refactor because you’ll need to take versions into consideration.
Be Gone! Away with versioning
JavaScript has an in-built backward-compatible that prevents us from introducing changes or legacy code that may break if new versions are released. It’s part of JavaScript’s robustness and prevention against breaking the web in general.
As a result, versioning is no longer a requirement, especially for new engines. However, legacy engines and compilers that are older than 10 years old might still need the versioning declarations. This prevents JavaScript from splitting into different versions and modes.
Old code will continue to be usable, regardless of releases.
However, JavaScript does have two modes — strict and sloppy — in order to deal with things like let
, which is a derivative and improvement on var
, but var
is not completely discontinued as it still has its place in both old and new code.
The issue with let
, for example, is that it wasn’t originally a reserved keyword, which means that developers could use them as variable names. This can cause some confusion, especially when you have syntax that looks something like this:
let = "Tibbers";
In the example above, let
is actually a typed var
instance. I know it’s weird but these things do happen.
Enter Strict Mode
Strict mode came in at version 5 (released 2009) and works on cleaning up legacy issues caused by non-reserved keywords used by developers prior to its release.
This will cause the code to break when previously legal syntaxes are used. Reserved keywords are implemented in a strict manner and will cause throwback errors if they are used incorrectly in the context of version 5 and above.
These reserved identifiers are: implements inteface package let private protected public yield static
Assignment to undeclared variables will cause a ReferenceError
to occur. In contrast, when undeclared variables are created, a global variable is automatically created to house it.
In strict mode, read-only properties also become enforced and will throw a TypeError
. Under a non-strict implementation, read-only properties only exist on a surface and syntax value. You can technically still change it, which can cause several unwanted side effects and the potential to break the purity of your code.
The issue with strict mode is that while it cleans up your JavaScript and enforces a higher level of code effectiveness based on the updated release, servicing your existing codebase to be compliant is another matter.
While the idea of strict mode is a good one for creating clarity and focus, whilst eliminating past legacy related issues, the act of upgrading, especially for larger codebases, can be quite problematic.
Final words
ECMAScript 6 is completely backward-compatible. So even if you accidentally mixed in older ideas into your JavaScript code, it won’t break unless you’re in strict mode.
It is also good to note that ECMAScript is a standard. This means that how engines and compilers implement it is a different entity. It is because browsers and companies behind the engines and compilers may interpret the standard differently from one another — such as how Microsoft always ends up with a different flavor of JavaScript, despite implementing JavaScript.
Developers and engines benefit from not having forks and versioning as it creates less overall load on the coding and implementation process.
While the difference between strict and non-strict is akin to versioning, in its own special way, it is less cumbersome than trying to maintain every version in addition to it. This is especially important as ECMAScript has moved to yearly releases since 2015, and even more recently, in 6 months cycles.