JavaScript: How far we’ve come

Long ago, in the age of CRT displays and trackball mice, the Internet was a primitive place. Users could create colorful web pages displaying GIFs and stylized text. It was a simpler time, when there were only a handful of browsers and JavaScript didn’t exist yet.

The age old dial up connection

There was only one language at the time, HTML, which was used to display content. The designers of Netscape Navigator, a precursor to Mozilla’s Firefox, believed that HTML needed a “glue language”, something that was easy to use by web designers and part-time programmers to bring dynamic content to your browser.

With that in mind, Netscape recruited Brenden Eich to create a programming language originally known as LiveScript. A prototype was created in 10 days and was shipped in the beta version of Netscape Navigator 2.0 in September of 1995. It was later renamed to JavaScript as a marketing ploy by Netscape as Java was a very popular language at the time.

… and 23 years later …

JavaScript is the number one most used programming language in the world.

It has been ported to server and desktop environments and has countless implementations. It has influenced the creation of many languages, some of which transpile back to JavaScript. The ecosystem itself is massive with NPM containing over 700,000 packages — the most in any package manager. It’s common nowadays to find developers who only use JavaScript from front to back. How has this come to be? How can the humble language of JavaScript have such a large following and community?

There are two factors at play here, necessity and domain.

JavaScript was not chosen by any majority. It was thrust upon us by Netscape Navigator and solidified by implementations in other browsers. It is the most popular language because there is no alternative. The internet is the business front for almost every company today. They need a website and JavaScript is what you must use to add dynamic content to an otherwise static page.

The combination of HTML, CSS, and JavaScript within a browser provides an interesting domain. Finding the best way to design and implement a website has proven to be an arduous process. Although slow in the beginning, countless libraries and frameworks have been created to solve this very problem. At various points in time, a JavaScript developer may tell you some particular framework was the best possible solution — they were all wrong. If you’ve been keeping up with the latest and greatest, React and Vue are the most popular. At one point in time, it was Ember. At another, Backbone.

If we are stuck with this language, the question then becomes, what is the best possible way we can write JavaScript given its domain? Perhaps the best way to answer this is diving into the history of JavaScript. What libraries came before? Why did they fail? What do we have today that is better?

The OG JavaScript

In the early 2010s, I had my first contact with front-end development. Like many developers, I experienced frustrations with cross-browser incompatibilities. I would usually find a concise and easily implementable solution and later discover that it didn’t work in Internet Explorer (oh how many hours were wasted on Internet Explorer). For example, adding CSS classes to an element:

element.classList.add('someClass');
element.classList.remove('someClass');

This worked well in every browser…except Internet Explorer 9. You would need to use className, a much more primitive property that contains a reference to the string value of the element’s CSS classes. Modifications to add and remove a CSS class were more complex and not as descriptive. For example:

// Add CSS class
element.className += ' someClass';
// Remove CSS class
element.className = element.className.replace(/\bsomeClass\b/g, '');

Using string concatenation and regex to add and remove a CSS class is enough to drive any sane developer mad — how can such a basic operation be so lengthy? Then, that same developer would wonder whether the code they wrote for the past hour even worked in Internet Explorer 9.

Another problem I faced was verbosity. Basic operations such as searching for an element on the page or performing an asynchronous API call proved to be trying. If I wanted to find an element, I would have to keep typing document.querySelector. If I wanted to perform an API call, it might look something like:

var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function()  {
if (this.readyState == 4 && this.status == 200) {
var myData = JSON.parse(this.responseText);
// Do something with data
}
};
xmlhttp.open("GET", '/api/something', true);
xmlhttp.send();

A lot of questions can be asked here. What does onreadystatechange mean? Why do we have to call so many functions? Why is it called XMLHttpRequest when we want JSON, not XML?

The Rise of jQuery

Both cross-browser incompatibilities and verbosity led to the rise of utility libraries — the king of which was jQuery.

jQuery turned document.querySelector into $. It turned className and classList into:

$('some-selector').addClass('some-class');
$('some-selector').removeClass('some-class');

jQuery also turned XMLHttpRequest into something much simpler:

$.ajax({
url: '/api/something',
type: 'GET',
success: function (response) {
// do something with JSON
}
});

From these two scenarios, I reasoned that there was no point in actually learning JavaScript — I could just learn jQuery! And for many developers, that is what happened. Almost every problem I searched on Google had a solution implemented in jQuery.

However, it didn’t solve all of JavaScript’s problems. When thinking about large front-end projects, a developer needs key features. One essential feature, was the ability to split JavaScript into separate files and modules based on functionality.

The OG JavaScript (revisited)

The domain of the browser was not designed to facilitate large front-end, single page applications strictly written in JavaScript like we see today. Scripts were placed side by side in the HTML with the idea that they would be loaded in series from top to bottom. The browser was designed with the idea of separating logic by languages instead of functionality. There was no way to import JavaScript from one script to another. All “imports” either happened using script tags or the global object. It just so happened that adding a property to the window global object automatically made it available everywhere. This may seem like a convenient feature, but I promise it is not. Let’s look at an example:

<script>
// Declare a place to store user information
window.user = {
...
};
</script>
<script src="some/third/party/library.js"></script>

Lets say the third party library I’m using in the second script tag also sets a property in the window object called user. It will overwrite our original data and there is nothing we can do about it. There were techniques at the time to avoid this, but the idea of using a global object to manage communication of data between scripts is precarious at best. So, jQuery didn’t solve all our problems. We needed to look at dividing the domain of the browser into code based on functionality instead of language.

The Backbone of JavaScript

Backbone was one of the first frameworks to tackle the issue of separation of concerns via functionality. Similar to back-end frameworks, Backbone ported over some of the principles of MVC architecture so that we could apply it to front-end development. Instead of storing data in a global variable or in the HTML, we could keep track of this information in models and collections. Instead of having to write HTML pages with jQuery manipulation, we could use views to render custom templates.

Templating was a big improvement to rendering out HTML as templates were reusable and offered a number of important features. HTML is just a markup language and therefore doesn’t have the ability to conditionally render content. We can’t loop over a collection and render HTML based on each entry. Originally, jQuery solved these issues, but it wasn’t enough. Having the HTML manipulations in JavaScript files made things messy and fragmented. Templating was a solution that allowed a JavaScript function to render a template with custom data passed to it. Each template contained HTML with syntax to conditionally render content or perform looping — similar to a PHP experience but in the browser.

Tying this all together with the ability to import JavaScript files, Backbone proved to be a formidable solution. And at this time, many developers rejoiced.

The Transition to ES6 and Components

Backbone and jQuery were both great libraries for their time. As front-end development progressed however, we saw the rise of new versions of the JavaScript language. ECMAScript (ES) is the standardized language that JavaScript is based on. ES6 is the 6th version of the language and recently came out in 2015. This release contained a plethora of updates and added features to JavaScript that many browsers have started supporting. Some features such as fetch and the import syntax have replaced the need for libraries. With the end of support for legacy browsers such as Internet Explorer, jQuery has taken a backseat as JavaScript itself has become much more consistent and fully-featured across all current browsers.

We’ve also seen the breakthrough of component driven architectures with the goal of combining JavaScript, HTML, and CSS into one file to render out a reusable component. What if the component could accept any number of arguments like a function, but also keep track of an internal state? What if we could hook into methods to perform operations when the component is updated? These questions gave way to a new library called React.

React gives the ability to write a JavaScript component with the following three properties:

  • The component accepts a number of inputs called props. Props can be updated which causes the component to be re-rendered automatically.
  • The component can keep track of it’s own state that is independent of props. Updates to state also cause re-renders.
  • The component can listen and handle updates to itself by using lifecycle methods.

React also pioneered the ability to write HTML-like syntax in JavaScript, a feature called JSX. Instead of having to write templates in a separate file, we can now return JSX in the component itself so that the JavaScript functionality, HTML structure, and CSS styles are tied together. For example, a Button component:

class Button extends React.Component {
render() {
const { isDisabled, text } = this.props;
const className = isDisabled ? 'button--disabled' : '';
    return (
<button className={className}>{text}</button>
);
}
}

In the above example, the render function is able to return JSX which is interpolated with various JavaScript variables that change based on the props passed to it. Every time the Button component receives an updated prop, it will re-render the JSX and return updated HTML.

Lifecycle methods can further enhance the functionality of a component by allowing operations to be performed at certain points in a component’s render cycle. We can use these methods to call APIs, perform DOM manipulations, and delete data as needed.

With features such as lifecycle methods, state management, and composing views together, React provides a better architecture to write front-end code compared to Backbone. React is written specifically for the front-end with new ideologies and is therefore a better fit than other frameworks.

Are ES6 and React the best possible solutions to solving the browser domain? Most likely ES6, but with React we may never truly know. Up and coming standards such as web components may bring React-like architecture directly to the browser so React may not be needed. Until that time though, we will keep tinkering with the browser domain and the JavaScript language to try to find the best way we can write front-end code — especially JavaScript.