close icon
JavaScript

Glossary of Modern JavaScript Concepts: Part 2

Learn about scope, closures, data flow, and concepts commonly utilized in modern JS frameworks & applications.

May 02, 2017

TL;DR: In the first part of the Glossary of Modern JS Concepts series, we learned about functional, reactive, and functional reactive programming. In Part 2, we'll gain an understanding of concepts like scope, closures, tree shaking, components, and more, as well as JavaScript application topics such as data flow and change detection.


Introduction

Modern JavaScript has experienced massive proliferation over recent years and shows no signs of slowing. Numerous concepts appearing in JS blogs and documentation are still unfamiliar to many front-end developers. In this post series, we'll learn intermediate and advanced concepts in the current front-end programming landscape and explore how they apply to modern JavaScript, JS frameworks, and JS applications.


Concepts

In this article, we'll address concepts that are crucial to understanding modern JavaScript and JS applications, including scope and closures, data flow, change detection, components, compilation, and more.

You can jump straight into each concept here, or continue reading to learn about them in order.


Scope (Global, Local, Lexical) and Closures

"Explain closures" is an infamous JavaScript technical interview question. The truth is that plenty of skilled JS developers have difficulty explaining closures, even if they conceptually understand (and even use) them. Let's back up and talk about the concepts necessary to explain a closure.

Scope

In order to grasp closures, we need to understand scope first. Scope is simply the context of our code: where variables and functions are accessible.

The following example demonstrates two scopes, global scope and local scope:

// Global scope
var globalVar = 'Hello, ';
console.log(localVar); // Uncaught ReferenceError: localVar is not defined

someFunction() {
  // Local scope
  var localVar = 'World!';
  console.log(globalVar + localVar); // 'Hello, World!'
}

Everything has access to the global scope. If we open an empty .js file and type var globalVar, this variable is accessible to anything else we'll create. If we executed the file in a browser, globalVar's function scope would be window.

Note: If we declare a new variable without the var keyword, it will be placed in the global scope no matter where it is in the code. You may have encountered this before (perhaps accidentally).

The someFunction function creates its own local scope. It also inherits access to the global scope. We can freely use globalVar inside someFunction. However, the global scope does not have access to nested contexts, such as someFunction's local scope. If we try to log localVar from the global scope, we will receive an error because localVar is not defined in the global scope.

In a nutshell, nested functions have their own scope. Functions declared inside another function also have access to their parent functions' scopes. This is called the scope chain.

Lexical scope (or static scope) refers to the fact that every nested function can access the functions that contain it.

Consider this example:

// Lexical scope and scope chain
var a = 1;

function outerFunc() {
  var b = 2;
  console.log(a + b);

  function middleFunc() {
    var c = 3;
    console.log(a + b + c);

    function innerFunc() {
      var d = 4;
      console.log(a + b + c + d);
    }

    innerFunc(); // logs 10 (1 + 2 + 3 + 4)
  }

  middleFunc(); // logs 6 (1 + 2 + 3)
}

outerFunc(); // logs 3 (1 + 2)

This code is available to run at this JSFiddle: JS Scope (open your browser's console to see results). innerFunc is the innermost function. It is declared inside middleFunc, which is in turn declared in outerFunc.

The innerFunc function can access variables declared in all of its parent scopes. Its scope chain allows access to:

  • a from the global scope,
  • b from outerFunc,
  • c from middleFunc, and
  • d from innerFunc's own local scope.

This only works down the nested functions, not up. For instance, the locally scoped variable d is declared in innerFunc and is not accessible to middleFunc, outerFunc, or the global scope.

Closures

In the first part of the Glossary of Modern JavaScript Concepts, we learned about higher-order functions and functions as first-class objects. If this doesn't sound familiar, take a moment to review the section on Higher-order Functions.

Now let's revisit the following higher-order function example we saw in Part 1:

// Higher-order function
function whenMeetingJohn() {
  return function() {
    alert('Hi!');
  }
}
var atLunchToday = whenMeetingJohn();

atLunchToday(); // alerts "Hi!"

This is a function that returns another function. Let's update this example to add an argument (salutation) and a greeting variable to whenMeetingJohn's local scope. We'll also name the previously anonymous returned function alertGreeting so we can refer to it more easily:

// Closures
function whenMeetingJohn(salutation) {
  var greeting = salutation + ', John!';

  function alertGreeting() {
    alert(greeting);
  }
  return alertGreeting;
}
var atLunchToday = whenMeetingJohn('Hi');

atLunchToday(); // alerts "Hi, John!"
whenMeetingJohn('Whassup')(); // alerts "Whassup, John!"

This code is available to run at this JSFiddle: JS Closures.

A closure is formed when a function (alertGreeting) declared inside an outer function (whenMeetingJohn) references variables from the outer function's local scope (such as the greeting variable).

The term "closure" refers to the function and the lexical environment (any local variables that were in scope when the closure was created) in which that function was declared.

When we execute atLunchToday(), we receive an alert with the argument we passed during assignment ('Hi' in this case) and the greeting variable that was accessible in alertGreeting's lexical environment.

Note: We can also call the returned function (alertGreeting) without assigning it. Doing so looks like this: whenMeetingJohn('Whassup')().

Hopefully you can see the value in closures when looking at this simple example. We can greet John with several different salutations. Each time, we create a closure with access to the particular salutation data in scope at the time of creation.

Another common example demonstrating closures uses a simple addition expression:

// Closures
function addCreator(x) {
  return function(y) {
    alert(x + y);
  }
}
var add1 = addCreator(1);
var add5 = addCreator(5);

add1(2); // alerts 3
add5(2); // alerts 7

This code can be run at this JSFiddle: JS Closures - Adder.

Both add1 and add5 are closures with different lexical environments storing different values for the x argument. These values are protected by the fact that they're "enclosed" in the lexical environment of each closure. We could use the addCreator(x) factory function to create as many add_ functions as we needed.

Scope and Closures Takeaways

Scope is something that many JS developers learn early on, but may not have had to explain in words or with specific examples. Understanding scope is vital to writing good JavaScript.

Note: There is more to scope than we covered here. There are some great resources available to achieve greater understanding, especially with regard to the this keyword. See below for more links.

Closures associate data with a function utilizing the lexical environment the function was declared in.

To learn more about scope and closures (and this), check out the following resources:


One-Way Data Flow and Two-Way Data Binding

With the proliferation of JavaScript frameworks and Single Page Applications (SPAs), it's important for JS developers to understand concepts like data flow / binding, and how the tools we're using manage this.

One-Way Data Flow

An application or framework with one-way data flow uses the model as the single source of truth. React is a widely recognized example of one-way data flow (or one-way data binding). Messages are sent from the UI in the form of events to signal the model to update.

Take a look at the following React example:

// One-way data flow with React
class OneWay extends React.Component {
  constructor() {
    super();
    this.handleChange = this.handleChange.bind(this);
    // set initial this.state.text to an empty string
    this.state = {
      text: ''
    };
  }
  handleChange(e) {
    // get new input value from the event and update state
    this.setState({
      text: e.target.value
    });
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.handleChange} />
        <p>Text: {this.state.text}</p>
      </div>
    );
  }
}

This code is available to run at JSFiddle: React One-Way Data Flow.

We can see that the state object model is established in the constructor function. The initial value of this.state.text is an empty string. In our render() function, we add an onChange handler to our <input> element. We use this handler to setState(), signalling the state object model to update the text property with the new value of the input field.

Data is only flowing in one direction: from the model down. The UI input does not have direct access to the model. If we want to update state in response to changes from the UI, the input must send a message carrying the payload. The only way the UI can influence the model is through this event and the setState() method. The UI will never automagically update the model.

Note: In order to reflect changes from the model to the UI, React creates a new virtual DOM and diffs the old virtual DOM with the updated virtual DOM. Only the changes are then rendered in the real DOM. We'll talk more about this in the section on change detection.

Two-Way Data Binding

In two-way data binding, the data flows in both directions. This means that the JS can update the model and the UI can do so as well. A common example of two-way data binding is with AngularJS.

Note: In this article, AngularJS refers specifically to version 1.x of the framework while Angular refers to versions 2.x and up, as per the Branding Guidelines for Angular.

Let's implement the same example from above, but with AngularJS two-way data binding:

// AngularJS two-way data binding
// script.js
(function() {
  angular
    .module('myApp', [])
    .controller('MyCtrl', function($scope) {
      // set initial $scope.text to an empty string
      $scope.text = '';
      // watch $scope.text for changes
      $scope.$watch('text', function(newVal, oldVal) {
        console.log(`Old value: ${oldVal}. New value: ${newVal}`);
      });
    });
}());
<!-- AngularJS two-way data binding -->
<!-- index.html -->
<body ng-app="myApp">
  <div ng-controller="MyCtrl">
    <input type="text" ng-model="text" />
    <p>Text: {{text}}</p>
  </div>
</body>

This code is available to run at Plunker: AngularJS Two-Way Binding.

In our controller, we set up the $scope.text model. In our template, we associate this model with the <input> using ng-model="text". When we change the input value in the UI, the model will also be updated in the controller. We can see this in the $watch().

Note: Using $watch() in a controller is debateable practice. We've done it here for example purposes. In your own AngularJS apps, take into consideration that there are alternatives to using $watch() in controllers (such as events), and if you do use $watch(), always deregister your watches $onDestroy.

This is two-way binding in AngularJS. As you can see, we didn't set up any events or handlers to explicitly signal the controller that the model was updated in the UI. The text data binding in the template automatically uses a watcher to display changes to the model. We can also $watch() the model. Watching should generally be done in services or directive link functions, not in controllers.

Note: AngularJS uses what's called the digest cycle (dirty checking) to compare a value with the previous value. You can read more about dirty checking in AngularJS in the section on change detection.

Aside: Two-Way Data Binding in Angular

But wait! Angular (v2+) has the "banana-in-a-box" [(ngModel)], right? On the surface, this may look like persistence of automagical two-way data binding. However, that is not the case. Angular's two-way binding [()] syntax simply shortcuts property and event binding in a template, and the ngModel directive supplies an ngModelChange event for you. To learn more about this, check out this article on two-way binding in Angular.

The following are functionally equivalent and demonstrate the ngModel directive:

// ngModel directive: two-way binding syntax
<input [(ngModel)]="text" />
<p>{{text}}</p>
// ngModel property and event binding
<input [ngModel]="text" (ngModelChange)="text=$event" />
<p>{{text}}</p>

The Angular docs on two-way binding cover this syntax thoroughly.

Data Flow and Binding Takeaways

Many modern JavaScript frameworks and libraries utilize unidirectional data flow (React, Angular, Inferno, Redux, etc.). Why? One-way data flow encourages clean architecture with regard to how data moves through an application. Application state is also easier to manage, updates are more predictable, and performance can be better as well.

Although automagical two-way data binding was one of AngularJS's most popular demos back in 2009, Angular has left it behind. Some Angular developers lamented this at first, but ultimately, many found that performance gains and greater control outweighed automagic.

As we saw in the React example above, it's important to remember that one-way data flow does not mean that it's difficult to update the store from the UI. It only means that such updates are done deliberately, with specific instruction. It's less magical, but much more manageable.

Note: Generally when developers mention "implementing two-way data binding" in frameworks with one-way data flow (such as React), they are referring to the steps necessary to have UI changes notify the state that it should be updated. They are not looking for a way to implement automagical two-way binding.

To learn more about one-way data flow and two-way data binding, check out the following resources:


Change Detection in JS Frameworks: Dirty Checking, Accessors, Virtual DOM

Change detection is important for any dynamic JavaScript Single Page Application (SPA). When the user updates something, the app must have a way to detect and react to that change appropriately. Some kind of change detection is therefore vital to SPA frameworks.

At a fairly high level, let's explore a few methods of change detection used in popular JavaScript frameworks today.

Dirty Checking

Although Angular was released, AngularJS still accounts for multitudes of apps in production or development right now. AngularJS uses what's known as the digest cycle to detect changes in an application. Under the hood, the digest cycle is dirty checking. What does this mean?

Dirty checking refers to a deep comparison that is run on all models in the view to check for a changed value. AngularJS's digest cycle adds a watcher for every property we add to the $scope and bind in the UI. Another watcher is added when we want to watch values for changes using $scope.$watch().

"AngularJS remembers the value and compares it to a previous value. This is basic dirty-checking. If there is a change in value, then it fires the change event."

Miško Hevery, creator of AngularJS and Angular

The digest cycle is a loop. AngularJS runs through its list of watchers and checks to see if any of the watched $scope variables have changed (aka, are "dirty"). If a variable has not changed, it moves on to the next watched variable. If it finds one that is dirty, it remembers its new value and re-enters the loop. When no new changes are detected in the entire watch list, the DOM is updated.

The major advantages of dirty checking are that it's simple and predictable: there is no extending of objects and there are no APIs involved. However, it's also inefficient. Whenever anything changes, the the digest cycle is triggered. Therefore, it's important that care is taken when creating watchers in AngularJS. Every time a $scope property is bound to the UI, a watcher is added. Every time a $watch() is implemented, another watcher is added. Many directives also add watchers, and so do scope variables, filters, and repeaters.

Though dirty checking is sufficiently fast in a simple app, we can easily see how this can get out of hand in a complex implementation. This has led to articles such as 11 Tips to Improve AngularJS Performance: 1. Minimize/Avoid Watchers and Speeding up AngularJS's $digest loop.

Note: Angular (v2+) no longer uses dirty checking.

Accessors

Ember and Backbone use data accessors (getters and setters) for change detection. Ember objects inherit from Ember's APIs and have get() and set() methods that must be used to update models with data binding. This enables the binding between the UI and the data model and Ember then knows exactly what changed. In turn, only the modified data triggers change events to update the app.

Note: In Backbone, this is done with Backbone models with get() and set() methods.

This method is straightforward and enforces that the application author be very deliberate regarding their data bindings. However, on the flipside of the same coin, it can occasionally lead to confusion because Ember.Objects are only used when data binding to templates. If there is no UI data binding, updates do not use Ember.Objects. This mixed approach can result in the developer scratching their head when things aren't updating because of a forgotten setter or getter.

Virtual DOM

Virtual DOM is used by React (and Inferno.js) to implement change detection. React doesn't specifically detect each change. Instead, the virtual DOM is used to diff the previous state of the UI and the new state when a change occurs. React is notified of such changes by the use of the setState() method, which triggers the render() method to perform a diff.

Virtual DOM (occasionally known as V-DOM) is a JavaScript data model that represents the real DOM tree. When a virtual DOM is generated, nothing is rendered to the browser. The old model is compared to the new model and once React determines which parts of the virtual DOM have changed, only those parts are patched in the real DOM.

Change Detection Takeaways

There are many ways that JavaScript frameworks manage change detection, including more that weren't covered here. They each have strengths and weaknesses, but the modern trend is toward more deliberate and less automagical methods, many utilizing observer pattern under the hood.

To learn more about change detection in JS frameworks, check out the following resources:


Web Components

Web components are encapsulated, reusable widgets based on web platform APIs. They are composed of four standards:

Web components allow us to architect and import custom elements that automatically associate JS behavior with templates and can utilize shadow DOM to provide CSS scoping and DOM encapsulation.

Web components consist of a set of web platform APIs. There are libraries (such as Polymer) and polyfills (such as webcomponents.js) to bridge the gap between current browser support and future web API support.

Let's say we want to create a simple web component (my-component) that shows some static text. We'd like to use HTML attributes to have the component change its text color and log something to the console. To display the <my-component> custom element in our website or app, we might import and use it like so:

<!-- index.html -->
<html>
  <head>
    ...
    <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.24/webcomponents.min.js"></script>
    <link rel="import" href="my-web-cmpnt.html">
  </head>
  <body>
    <my-web-cmpnt color="red" log="Hello"></my-web-cmpnt>
    ...

To create the my-component web component utilizing shadow DOM, our my-component.html might look something like this:

<!-- my-component.html -->
<template>
  <style>
    .my-component {
      display: block;
      padding: 20px;
    }
  </style>
  <div class="my-component">
    <p>This is a custom element!</p>
  </div>
</template>
<script>
  (function(window, document, undefined) {
    var doc = document;
    // my-component document
    var self = (doc._currentScript || doc.currentScript).ownerDocument;
    var template = self.querySelector('template').content;
    // ShadowCSS shim, if needed
    if (window.ShadowDOMPolyfill) {
      WebComponents.ShadowCSS.shimStyling(template, 'my-component');
    }
    class MyComponent extends HTMLElement {
      constructor() {
        super();
      }
      connectedCallback() {
        // get attributes
        var color = this.getAttribute('color');
        var log = this.getAttribute('log');
        // utilize shadow DOM
        var shadowRoot = this.attachShadow({mode:'open'});  
        var clone = doc.importNode(template, true);
        var myComponent;
        shadowRoot.appendChild(clone);
        myComponent = shadowRoot.querySelector('.my-component');
        // style with color and output log
        myComponent.style.color = color;
        console.log(log);
      }
    }
    window.customElements.define('my-component', MyComponent);
  }(window, document));
</script>

This code is available to run at Plunker: Web components.

The <template> defines the element's CSS styling and HTML markup. Then, to take advantage of shadow DOM and JS functionality, we add a <script> tag to our my-component.html file after the closing </template> tag and implement our desired custom element JS functionality to get the element's attributes and use them to change the text color and log our message.

When inspected with Chrome devtools, our component looks like this:

modern js glossary: custom web component in Chrome inspector

For a much more indepth tutorial, check out Web Components: How To Craft Your Own Custom Components.

Web components bring powerful, framework-like capabilities to browsers, and while the spec and support are still being finalized, the concepts have inspired frameworks such as Angular (which initially attempted to use Web API web components, but ended up with its own implementation). Many JS frameworks (Angular, React, Ember, Vue) leverage the concept of componetization with varying degrees of similarity to the web components API.

Web Components Takeaways

Web components allow us to create and use custom HTML elements with JS. They can also utilize the shadow DOM to provide CSS scoping and DOM encapsulation. By themselves, web components alone aren't a substitute for all the features of a SPA framework. However, their core concepts are heavily leveraged by many frameworks in use today and their future in the front-end landscape offers many opportunities for a growing community of resources.

To learn more about web components, check out the following resources:


Smart and Dumb Components

As we talked about above, some modern JS frameworks are heavily component-based. This leads to concepts of component architecture and component communication. In some cases (such as in React and Angular), component architecture utilizes smart and dumb components. They are also referred to as "container" (smart) and "presentational" (dumb) components.

Smart Components

Also known as container components, smart components can manage interactions with the application's state and data. They handle business logic and respond to events emitted from children (which are often dumb components).

Dumb Components

Also known as presentational components, dumb components rely on inputs supplied by their parents and are ignorant of application state. They can be sometimes be considered pure and are modular and reusable. They can communicate to their parents when reacting to an event, but they don't handle the event themselves.

Presentational and Container Components in React

Dan Abramov, the co-author of Redux, Create React App, React Hot Loader, and more, originally wrote about presentational and container components and their meaning in React, particularly when used with state management like Flux or Redux. In a nutshell, the concepts can be summarized as follows:

Presentational (aka Dumb) Components:

  • Focus on "How things look"
  • Allow containment with this.props.children
  • No dependencies on the rest of the app (ie., no Flux or Redux actions or stores)
  • Only receive data; do not load or mutate it
  • Are generally functional (with exceptions)

Container (aka Smart) Components:

  • Focus on "How things work"
  • Provide data and behavior to other components
  • Usually have no or very little DOM markup, and never have styles
  • Are often stateful data sources
  • Are generally generated from higher order components

Check out his article for more details and explanation.

Example: Smart and Dumb Components in Angular

For a quick example that doesn't involve a state container, let's look at some simple smart and dumb components using Angular.

Let's say we want a madlib-style feature where we apologize for lashing out while hungry, tired, or debugging. When we're done, it should look like this in the browser:

modern js glossary: Angular smart and dumb components

The smart (container) component looks like this:

// app/smart.component.ts
import { Component } from '@angular/core';
import { DumbComponent } from './dumb.component';

@Component({
  selector: 'my-smart-cmpnt',
  template: `
    <h1>I'm sorry for what I said when I was {{selectedOption}}.</h1>

    <my-dumb-cmpnt
      [options]="optionsArr"
      (changedOption)="onOptionChange($event)"></my-dumb-cmpnt>
  `
})
export class SmartComponent {
  optionsArr = ['hungry', 'tired', 'debugging'];
  selectedOption = '______';

  onOptionChange(e: string) {
    this.selectedOption = e;
  }
}

This component manages all of the data necessary for this feature. It provides the options for the madlib (optionsArr) and then handles when the user chooses an option (onOptionChange()). It stores the selectedOption, passes the possible options into the dumb component, and sets the selected option when the dumb component emits a changedOption event.

// app/dumb.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'my-dumb-cmpnt',
  template: `
    <div *ngFor="let option of options">
      <button (click)="select(option)">{{option}}</button>
    </div>
  `
})
export class DumbComponent {
  @Input() options: Array;
  @Output() changedOption = new EventEmitter();

  select(option) {
    this.changedOption.emit(option);
  }
}

In turn, the dumb component accepts the options array as input and iterates over each item to create the buttons to select an option. When an option is clicked, the changedOption event is emitted with the selected option as its payload. The parent smart component then handles this event and sets its selectedOption for display in the UI.

This code is available to run at Plunker: Angular Smart and Dumb Components.

Smart and Dumb Components Takeaways

Smart (container) components manage data, implement business logic, and handle events. Dumb (presentational) components accept inputs and can emit events, which are handled by a parent smart component. Dumb components are modular and can be reused throughout an application due to their more stateless nature. When using a state container like Redux or ngrx/store, only smart components would dispatch actions or interact with the store.

To learn more about smart and dumb components, check out the following resources:


JIT (Just-In-Time) Compilation

Just-In-time (JIT) compilation is the process of translating code written in a programming language to machine code at runtime (during a program or application's execution). At runtime, certain dynamic information is available, such as type identification. A JIT compiler monitors to detect functions or loops of code that are run multiple times—this code is considered "warm". These pieces of code are then compiled. If they're quite commonly executed ("hot"), JIT will optimize them and also store the optimized, compiled code for execution.

When the compiler optimizes hot code, it makes assumptions about its types and shape based on consistency of previous executions. At any iteration, if those assumptions turn out to be inaccurate, the optimized code is discarded.

Browsers use JIT compilation to run JavaScript. In the modern JavaScript framework landscape, compilers in frameworks like Angular can use JIT to compile TypeScript and Angular code to JS to machine code at runtime during local development, compiling each file separately. This provides certain advantages, such as no need to rebuild the project when watching for code changes and a faster initial build time.

JIT Compilation Takeaways

JIT compilation is used by browsers to compile JavaScript at runtime, and by frameworks like Angular to provide a fast local development experience.

To learn more about JIT compilation, check out the following resources:


AOT (Ahead-Of-Time) Compilation

Ahead-Of-Time (AOT) compilation is the process of translating code written in a programming language to machine code before execution (as opposed to at runtime). Doing so reduces runtime overhead and compiles all files together rather than separately.

With regard to JavaScript application AOT, such as that used by Angular, this means inlining HTML and CSS and being able to deploy without the compiler, saving considerable size. The browser can also render the application immediately since it's precompiled.

There are several benefits to AOT for production builds:

  • Fewer asynchronous requests: templates and styles are inlined with JS
  • Smaller download size: the compiler doesn't need to be downloaded if the app is already compiled
  • Detect template errors earlier: compiler detects binding errors during build rather than at runtime
  • Better security: evaluation is already done, which lowers chance of injection

AOT also enables tree shaking. In the browser, an app compiled with AOT promotes a shorter total time to load and bootstrap due to being precompiled. Initial rendering time is also reduced because less code needs to be parsed.

However, AOT will have a longer initial build time than JIT and would require a full recompile of the entire app if any changes are made.

AOT Compilation Takeaways

AOT compilation takes place before runtime and bundles all files together. It enables tree shaking, smaller downloads, and improved security compared to JIT.

To learn more about AOT compilation, check out the following resources:


Tree Shaking

Tree shaking is a JavaScript module bundling term that refers to the static analysis of all imported code and exclusion of anything that isn't actually used.

In a more literal analogy, consider a living tree. The tree is shaken and this causes the dead leaves to fall off, leaving behind the leaves the tree is actively using for photosynthesis. The concept behind tree shaking is live code inclusion: we include the parts that are needed to begin with, as opposed to removing the parts that are unneeded at the end (dead code elimination).

Tree shaking relies on ES2015 module import and export. The import and export statements compose an app's static module structure. When the modules are bundled for deployment, the tree shaker analyzes the static module structure so that unused exports can be excluded, reducing the size of the final bundle.

ES2015 enables us to specify explicit imports. For example, rather than importing the entire RxJS library, we can only import exactly what we want:

import { BehaviorSubject } from 'rxjs/BehaviorSubject';

This differs from the dynamic require statement used by CommonJS or AMD. Tree shaking uses this principle to walk the dependency graph and exclude things that aren't needed in order to reduce the size of deployment bundles.

The principle of tree shaking is not new, but it has recently been popularized by the rollup.js module bundler. Tree shaking is utilized in Webpack 2 as well. The concept of tree shaking and writing code that promotes it is also prevalent in Angular withAOT compilation.

Tree Shaking Takeaways

Tree shaking is term for JavaScript live code inclusion in module bundlers that use ES2015 static import and export to "shake out" unneeded dependencies on a more granular level.

To learn more about tree shaking, check out the following resources:


Aside: Learning and Implementing Authentication with Auth0

One of the most complex features to implement in an application is user authentication and identity management. Security for authentication and identity is an entire glossary unto itself.

Auth0 hosted login screen

If you need to implement a robust, highly customizable identity and access management system quickly and easily for your JavaScript SPAs and Node APIs, Auth0 can help. Auth0 provides Single Page Application QuickStart guides, an SDK for web, plenty of documentation, and this blog's articles and tutorials. You can sign up for a free Auth0 account here to get started.

Auth0 offers a generous free tier to get started with modern authentication.


Conclusion

With the swift rise of JavaScript Single Page Application frameworks and component-based paradigms, it's important to understand JS topics relating to scoping, data flow, components, compilation, and bundling.

With this glossary as a starting point, you can begin taking advantage of these concepts and programming paradigms to increase your JavaScript expertise. If anything is still unclear regarding these topics, please consult the links in each section for additional resources. You can also check out the first part of the Glossary of Modern JS Concepts to learn about the concepts necessary to understand functional programming, reactive programming, and functional reactive programming.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon