What’s New in React 16

Introduction

Facebook recently announced the release of React 16, a complete rewrite of the React library. The new core architecture, code-named Fiber, keeps most of the API intact and backward compatible, so you can update existing apps without running into issues. Fiber introduces lots of new highly anticipated features and improvements to React. This blog post is about what’s new in React 16. It will cover the new features that lets React developers catch and handle JavaScript errors in React components; render elements into different locations in the DOM outside of the main component tree using portals; return multiple elements without having to wrap them in a parent element using new render return types like arrays, strings and numbers; also return null from setState to prevent unnecessary state updates and re-renders and more.

You can start using React 16 today. When you install React in a project or create a new app using the create-react-app tool, it will automatically install the latest version of React and ReactDOM. As mentioned above, React 16 is backwards compatible. So if you are currently running your app on a previous version of React without any console errors or deprecation warnings, updating to React 16 will not break your app. It should be a seamless transition. Now with a complete rewrite of the React Library comes a few minor breaking changes that only affect uncommon use cases. You can view all the breaking changes. A welcome change is that React is now available under the MIT license. The uncertainties created by Facebook’s previous BSD+Patent’s open source license was a concern to many developers, startups and companies using React. So now React licensing is more flexible. You can use React in your project without having to worry about the previous patent’s clause. Now if you are not able to upgrade immediately to version 16, React 15.6.2, was also published under MIT. Also note, the React 16 update does not mean that whatever you have learned so far about React is immediately obsolete. The API itself hasn’t changed, and most of the skills you have learned are still important, fundamental skills you will continue to use. So if you are eager to learn about everything that is new in React 16 next I will cover the new component life cycle method and error handling feature that lets us catch and manage errors when rendering React components.

Take Control of Errors with componentDidCatch()

One of the biggest changes in React 16 is how React handles JavaScript errors. Normally, a JavaScript error inside a component, for example, like an uncaught type error will break the entire app. A JavaScript error in a part of the UI shouldn’t break the whole app. So in React 16 error handling was improved to avoid these types of situations where runtime errors during rendering breaks your app. It provides a built-in solution for handling errors gracefully with a new life cycle method called componentDidCatch(). This life cycle hook gets triggered whenever a childs component render or life cycle methods throw an error. When an error occurs, componentDidCatch() catches the error and instead of crashing the entire app, the app continues to run as normal displaying all the UI except for the faulty component. You can even display a fallback UI to replace the component that crashed.

Catching Errors with Error Boundaries

With the componentDidCatch() lifecycle method comes a new concept of an error boundary. Error boundaries are wrapper components that use componentDidCatch() to capture errors anywhere in their child component tree and display a fallback UI in its place. They provide an easier way to keep the error catching and conditional rendering logic reusable and maintainable in your app. You create an error boundary as a class component. Any errors caught in the component tree get reported up to the Error Boundary’s componentDidCatch() method thereby providing a handy way of sending error reports to an error tracking or monitoring service. The componentDidCatch() method take two arguments to help you track and investigate errors, error – the error instance itself, and info which contains the component stack trace or the path in the component tree leading up to the offending component. The component stack trace contains helpful information for sorting and resolving errors. The componentDidCatch() method and Error Boundary work like try/catch statements for your React components. They provide a more consistent and dependable way to catch and deal with errors.

Note: Error Boundaries only catch errors in the components below them in the tree. An error boundary can’t catch an error within itself.

New Return Types

React 16 supports new return types that let your render less DOM nodes. For example, React no longer requires that the render() method returns a single React element. You are able to render multiple sibling elements without a wrapping element by returning an array. The other new return types React 16 supports are strings and numbers. Eliminating divs or any extra element added just to wrap your React component tree or returned content leads to overall cleaner and smaller components.

Render Children into Other DOM Nodes with Portals

React 16 introduces a new way of rendering into the DOM. You can render children into a DOM element that exists outside of your apps main DOM tree with a new feature called Portals. For example, in your index.html template you will usually have a div with the id root which is where your entire app is rendered into the DOM via ReactDOM.render. To create a portal, you add a second element that’s a sibling of your root div as shown below.

<div id="root"></div>
<div id="my-portal"></div>

Then you can render specific parts of your component tree inside the sibling div with the new createPortal() function. The createPortal(child, container) function accepts two arguments, child and container. The first argument child is the content you want to render. The second argumnet container is the DOM element where the content is going to render. createPortal() provides a hook into HTML that’s outside the root div. As mentioned in React docs, portals are useful when a parent component has an overflow hidden or z-index style, but you need the child to visually break out of its container. So a handy use case for portals is when creating modal windows or overlays. What’s interesting about portals is that the components they render are recognized as regular child components of the app. In other words, even though portal can be anywhere in the DOM tree, it behaves like a normal React child in every other way. For example, an event triggered from inside a portal still propagates or bubbles up to the parent component.

Returning null from setState

React 16 lets you decide if state gets updated via setState to prevent unnecessary DOM updates. In your events you can check if the new value of the state is the same as the existing one. If the values are the same, you can return null. Returning null will not update state and trigger a re-render. React 16 provides state performance improvements that let you prevent an update from being triggered by returning null for setState if the new value of state is the same as the existing value. Preventing unnecessary state updates and re-renders with null can make your app perform faster.

So these are some of the most important updates to React 16. Now there are a few other features I didn’t cover above like support for custom DOM attributes and Better server-side rendering. Finally, as with many popular JavaScript libraries, React is going to be updated frequently. Happy Coding!!!

Resources

Asynchronous Programming – The End of The Loop

To become an effective JavaScript programmer, it is important for a developer to learn how to develop and maintain asynchronous programs. JavaScript is a single-threaded programming language due to which applications written in JavaScript must use async APIs to stay responsive to user inputs while performing long-running tasks such as making a request for data from a server or running animations. You can’t get very far in a JavaScript code base without running across an asynchronous API.

Asynchronous programming may seem intimidating. How can we write programs that accepts input from the user, runs an animation, and sends a request to the server over the same period of time? How do we keep the code base clear and concise? How do we gracefully propagate and handle asynchronous errors? How can we avoid memory leaks caused by dangling event handlers? The different kinds of loops i.e. `for`, `for/in`, `while` and `do/while` and `try/catch/finally` statements in JavaScript are no help since they only work on synchronous functions.

Asynchronous programming is much easier than it seems and the key to it is to think differently about events. By using a handful of simple functions it is possible to build asynchronous programs. The first secret towards mastering asynchronous programming is learning to write programs without making use of loops. JavaScript loops can only work synchronously, and therefore cannot be used to repeat asynchronous functions. In order to gain expertise in asynchronous programming we must first learn how to code without making use of loops.

In the upcoming 9 posts to follow we will learn how to program Arrays without loops using just a few simple functions. We will learn the correct approach towards asynchronous programming and avoid making common mistakes. By the end of these 9 posts we will have the tools, concepts, and libraries required to be an asynchronous programming expert!

An Introduction to Yarn Package Manager

In this post, I will introduce you to the Yarn package manager. I will show how to integrate Yarn in your workflow and how to take advantage of its faster performance and consistency. NPM transformed the way we download, install and manage our application’s dependencies. But as you know it is not a perfect tool, even if there was such a thing. It’s slow and inconsistent and it has some security concerns as well. Well Yarn aims to fix that. Over the next few minutes I am going to introduce you to Yarn: A new package manager for JavaScript. Now lets get this out of the way Yarn does not attempt to completely replace NPM. Yarn is a new command line interface designed by the people at Facebook, Google, Exponent and Tilde and it fetches packages from the NPM registry. But it will replace NPM’s command line interface allowing you to do anything that the NPM Client provides.

Getting Started with Yarn is straightforward and of course the first thing that you need to do is install Yarn on your system. Visit Yarn’s website @ https://yarnpkg.com/ and then Click on Install Yarn button. This is going to take you to the installation instructions @ https://yarnpkg.com/en/docs/install for Yarn on your Operating System. Now Yarn needs NodeJS installed. More specifically, it needs NPM. So a lot of these installation instructions are solutions that will also install Node for you. For example, if you are on macOS by using Homebrew you can install Yarn which will also ensure that NodeJS is installed if it’s not already. If you are on Windows, if you install via Chocolatey then it will also ensure that NodeJS is installed. But if you download the installer be sure that NodeJS is installed first. Now this is just one way and chances are very good that you already have NodeJS installed on your computer. So you can install Yarn through NPM. Basically,

$ npm install -g yarn

and that’s going to give you the Yarn CLI. Once you have this installed then all you need to do is use the Yarn command and we will go over the commands that will be replacing NPM in your workflow because that’s essentially what Yarn is going to do. Now when creating a new project the first thing we do is initialize it with npm init and then we go through the process of answering questions and then we end up with a package.json file. And we essentially do the same thing with Yarn. Enter yarn init within the root of your project directory and then we answer the same questions. Now the interface looks a little bit different. Now we still end up with the same package.json file. So let’s say that we want to add React to our project. With NPM you would enter npm install –save react react-dom. With Yarn, we enter the command yarn add react react-dom. Saving is the default here. So we don’t have to say/enter that we want to save it. It is going to be default and it is going to download those dependencies for us. And it’s also going to be faster too because that’s one of the nice things about Yarn.

It’s performance actually comes from 2 separate things. The first is that everything that we install is going to be cached on our system. So whenever we want to install it with another project the first thing it is going to do is check to see if there is a new version. If it’s not new then it is going to use what we have in our cache and that is of course going to be much faster then it is by downloading it over the wire. But the second thing is that Yarn processes in parallel, NPM does not. NPM does things serially which makes sense if one task dependent upon another. But that’s not the case a lot of or even most of the time. So Yarn processes things in parallel which of course is going to be faster. Now I just added react above. Let’s remove that and we would do so by entering yarn remove react react-dom within the root of your project directory. It’s going to remove those packages. But now let’s add them. Let’s say we want to add a specific version, for e.g., version 15.3.1 of react and react-dom. So we will enter yarn add react@15.3.1 react-dom@15.3.1 and that will install that particular version for our project.

Once this is done installing, we are going to look at not just the package.json file but another file called yarn.lock. Because the yarn.lock file is very important with Yarn. First of all the package.json file is standard. We have the dependencies listed as well as their versions. Now one of the problems with NPM is that whenever your committed this to your repository and somebody else cloned it and did npm install and of course it is going to install the dependencies but you aren’t always guaranteed to get that exact version. In some cases, it might download a patch for that version which then you have 2 people working on essentially the same project but using different dependencies and that is a huge problem. So the folks that created Yarn decided to create this lock file. If you look at yarn.lock, the very first thing it says THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. This is created by Yarn whenever you install dependencies and it specifies the package as well as the version. So this is the file that you definitely want to commit to your repository because then whoever installs the packages using Yarn is going to get the same exact packages that are mentioned within the lock file. So if you look into yarn.lock file you will see both react & react-dom of version 15.3.1 installed.

Let’s say we are ready to update to version 15.4.0. For that we enter yarn upgrade react@15.4.0 react-dom@15.4.0 and this is going to update just those dependencies. Looking back into yarn.lock you will see those updated version numbers. So anytime you add, remove or upgrade a package, Yarn is going to update the lock file so that all the versions for all the dependencies are going to be consistent for everybody working on the same project and that is extremely important. That is also the main reason why Yarn was created to begin with. Now its worth noting that the packages that Yarn installs are from the NPM registry. There is not this new registry that have yarn packages. It’s using NPM. So in that sense there are a lot of related commands that are the exact same as NPM.

For example, we have already seen yarn init. There is also link, there is outdated, publish, run, cache clean, login, logout and test. All of these are the same commands as you would run with npm. However, Yarn has few things which NPM does not. For example, if we wanted to inspect all the licenses of our dependencies we would enter yarn licenses ls and it would list all our dependencies, their versions, their license and the URL for their repository. We can also generate our license dependency disclaimer by entering yarn licenses generate-disclaimer and that would generate that disclaimer. Finally, we can find out why we have a particular dependency. For example, let’s say we have a dependency called promise and so using  yarn why promise we can see why we have this promise dependency. Running yarn why promise gives us one of the following info i.e. This module exists because “react#fbjs” depends on it. 

We live in a world where there are new tools released almost on a daily basis and many of those tools are questionable. I mean there is no question that they are useful but you have to question whether or not it’s something that you want to spend time on. Well, Yarn is not one of those tools. Yes, it replicates NPM’s functionality  but it also brings some very useful things to the table. I mean the performance alone is the reason to use Yarn. So install it today and use it in your workflow.

React Life Cycle

This post covers some of the key concepts in React – props, state and keys. Then I will talk about React’s multiple life cycle functions which give us hooks for initializing our components and attaching behaviors at specific points in time.

Data for a given React component is held in two places, props and state.

Props is short for properties. We can think of properties a lot like HTML attributes. Props allow us to pass data down to child components. Props are immutable since they are passed down from the parent, they are effectively owned by the parent.

State in contrast is mutable and since state is mutable we should only strive to utilize state on our controller views. In other words, only use state on the top level components. Pass data down to your child components via props.

getInitialState – We can optionally define a getInitialState function. In this function, we can set the initial state for our component. This should typically only be done in the top level component, also known as our controller view.

getDefaultProps – We can also define default values for properties on our components using the getDefaultProps method. Inside this method, we can return a set of properties that our component should use by default if the parent component doesn’t declare a value.

React’s Life Cycle Methods

componentWillMount
When – Runs immediately before initial rendering. Runs both on the client and server.
Why – This function is a great spot to set the component’s initial state.

componentDidMount
When – Runs immediately after render.
Why – By the time this function is called, the component’s DOM exists. So this is a handy spot for integrating with other frameworks such as third party component libraries. This is also a good spot to set timers and make AJAX requests since you now know the component is rendered in the DOM.

componentWillReceiveProps
When – Run when the component is receiving new properties. In other words, when properties have changed. Not called on initial render.
Why – This is a place to set state before the next render since this runs just before the new properties are received.

shouldComponentUpdate
When – Runs immediately before render and when props or state are being received by your component.
Why – The big reason this function is useful is for performance. Why? Well, sometimes props and/or state changes, but the component doesn’t actually need to re-render because the data change doesn’t affect the DOM. When this is the case, you can return false from this function to avoid an unnecessary render call.

componentWillUpdate
When – Runs immediately before rendering when new props or state are being received. Not called on initial render.
Why – This function is a useful place to prepare for an update. However, do note, we cannot call setState in this function.

componentDidUpdate
When – Is evoked immediately after the component’s updates are flushed to the DOM. Not called for the initial render.
Why – This function allows us to operate on the DOM immediately after the component has been updated and re-rendered in the DOM.

componentWillUnmount
When – This function runs just before the component is unmounted/removed from the DOM.
Why – This is a great place to cleanup by destroying any related resources or DOM elements that were created when the component was mounted.

Keys for Dynamic Children

When creating multiple child components dynamically, we need to provide a key for each child component. The key is often the primary key for the corresponding database record, but it need not be. We just need to declare a unique ID for each specific record. As child components are added and removed, React uses this key to assure that child components are properly reordered or destroyed.

Object-Oriented JavaScript

Object

An object is a container for values in the form of properties and functionality in the form of methods.

Objects

  • Provide functionality through methods. Methods may or may not return values.
  • Provides data storage in properties
  • The name of the property is a key
  • The contents of a property is known as a value

Objects can be categorized into three distinct kinds of objects

  1. Native Objects – These objects are native to JavaScript. Some examples of these native objects are Number, String, Array, Boolean & Object. No matter where the JavaScript program is run, it will have these objects.
  2. Host Objects – These objects are provided by the environment also known as the host environment, where the JavaScript program is running. The browser has hundreds of different host objects. For e.g., document, console, Element, etc.
  3. Your Own Objects

Object Literal

Object literals are one common way to create objects. An object literal holds data or information about a particular thing at a given time. Object keys or property names in JavaScript are of type String. You use dot notation or square bracket syntax for accessing the values of keys or property names. If the key or property name is not a valid JavaScript variable name then its value can only be accessed via square bracket syntax. For e.g., an object with a key of “full name”, its value would be accessed using person[“full name”].

Understanding this

To programmatically modify the state of an object or call one of it’s methods from itself we use the keyword this.

Creating Multiple Instances with Constructors

Object literals aren’t the only way to create objects. Object literals are handy when creating one off objects or passing values to a function. Maintaining code with several object literals of the same type can get cumbersome. If you want to create multiple objects of the same kind or type, we need to make use of Constructor functions. A constructor function describes how an object should be created & it will create similar looking objects. Each object created is known as an instance of that object type. An instance is the specific realization of a particular type or object. Constructor functions help in organizing the code by preventing repetition thereby keeping the code DRY.

Prototypes

A Prototype is basically an encapsulation of properties that an object links to. So we can have properties and methods that are on an object’s prototype and for every copy of that object that we have it all links back to the same prototype. It’s much more efficient to have one copy of all of those object methods out there sitting inside the object prototype.

Methods with Prototypes

JavaScript provides a way to organize our code with constructor functions using a special property called prototype. The prototype on a constructor function is an object like an object literal. JavaScript is known as a prototypal programming language which means you can use prototypes as templates for objects for sharing values and behaviors between instances of objects. The prototype property allows us to add properties and methods to objects with the same constructor function.

Prototypal Inheritance

The technique used in JavaScript to share functionality between similar types of objects is known as Prototypal Inheritance.

Consider an application which has objects with similar properties and behavior. Creating separate constructor functions for each type of object would result in a lot of work, especially if the objects share the same behavior. Fortunately, we can use Inheritance to solve this problem and make our programming simpler. Inheritance is a programming technique that lets you share the same code between similar types. For example, if we wanted to extend the playlist application to include movies as a playlist item, both Song and Movie objects have the following properties – title, duration, isPlaying. They also have same behaviors such as play() and stop(). The differences are, say for example, that the song has an artist and the movie has a year. For all this common code, we could create a Media type with title, duration, and isPlaying properties. It can also have play and stop behavior. We can then link it to other types of media, in our case it’s the song and movie. This linking is called creating a prototype chain. When you access a property or method on an object, the JavaScript interpreter checks if it’s directly on the instance. If it’s not, it will check the prototype. If it’s not there, it will check the next prototype up the chain.

 

ES2015 – Destructuring

When we declare variables with var, let and const we typically also want to initialize the variable by assigning a value. One of the new features of ES6 is the ability to use a destructuring assignment. Destructuring is an operation you might have seen in other languages like Python or Ruby. The destructuring operation allows us to assign values to a set of variables by destructuring or literally tearing apart and pattern matching a complex data structure like an array or an  object full of properties.


describe("destructuring",function(){
"use strict";
it("can destructure arrays",function(){
let x = 2;
let y = 3;

[x, y] = [y, x];

expect(x).toBe(3);
expect(y).toBe(2);
});
});

The line [x, y] = [y, x]  is a destructuring assignment which says give the variables x and y the values y and x. We are not creating a new array but instead are moving the value 3 from the y into the x and the value of 2 from the x into the y. The end result is that we swapped those values in x and y. This is one example of destructuring but there is lot more that we can do.

It is very important to understand that what we see on the right hand side of the destructuring assignment is an array. It’s an array built with the values that are in y and x. What we see on the left hand side of the destructuring assignment is not an array. The syntax looks like as if we are am building an array literal but instead we are just working with individual variables x and y. They are surrounded by square brackets since we are destructuring an array. In other words we are telling JavaScript to take the first value of that array and put it into x  and take that second value of that array and put it into y.

It is a little more obvious that we are working with individual variables if we get rid of those 2 lines of code that explicitly create x and y and instead use the let statement to say let there be a variable x that will be initialized to have the first value in some array and let there be a variable y that takes the second value as show below.


it("can destructure arrays",function(){
let [x, y] = [3, 2];
expect(x).toBe(3);
expect(y).toBe(2);
});

We can even have this array [3, 2] be returned from a function as shown in the below function.


it("can destructure arrays",function(){
var doWork = function(){
return [3, 2];
}
let [x, y] = doWork();
expect(x).toBe(3);
expect(y).toBe(2);
});

If the contents within the array were [1, 3, 2], running the below test would result in a fail since x is going to get the value of 1.


it("can destructure arrays",function(){
var doWork = function(){
return [1, 3, 2];
}
let [x, y] = doWork();
expect(x).toBe(3);
expect(y).toBe(2);
});

If that’s not what we want, if we want x to take the second value and y to take the third value inside of the destructuring assignment we can use a comma with no symbol there in any position where we want to skip the value as shown in below code snippet. We can place the empty comma in the beginning, at the end or anywhere in the middle.


it("can destructure arrays",function(){
var doWork = function(){
return [1, 3, 2];
}
let [, x, y] = doWork();
expect(x).toBe(3);
expect(y).toBe(2);
});

Along the same lines, if we had an additional variable z that’s trying to get to a member of that array that doesn’t exist. Its not returned by doWork because it only returned 1,3 and 2. What would be the value of z to be? Well, generally in JavaScript if we try to get to something that is not there like the return value of a function that doesn’t return something or the value of a function parameter that wasn’t passed, you will get undefined and that’s also what happens in the below code snippet. Running the below test results in a pass because z will get an undefined value.


it("can destructure arrays",function(){

var doWork = function(){
return [1, 3, 2];
}

let [, x, y, z] = doWork();
expect(x).toBe(3);
expect(y).toBe(2);
expect(z).toBeUndefined();
});

This is how array destructuring works. Now lets look at a similar scenario where we have a function called doWork but this time instead of returning an array, its returning an object with firstName, lastName and twitter properties.


it("can destructure objects",function(){
let doWork = function(){
return{
firstName: "Vivekanand",
lastName: "Rao",
twitter: "viveksrao"
};
};
let { firstName: FirstName,twitter: TwitterHandle} = doWork();
expect(FirstName).toBe("Vivekanand");
expect(TwitterHandle).toBe("viveksrao");
});

Now what can be a little bit confusing about this destructuring – let { firstName: FirstName,twitter: TwitterHandle} = doWork(); is it looks like we are building an object literal which is not the case. Instead we are defining individual variables and assigning them values from the object that is returned from doWork. Because it’s an object that’s why we make use of the curly braces. In this syntax let { firstName: FirstName,twitter: TwitterHandle} = doWork(); the way to think about this is to say we are going to take the value of the firstName property from the object returned by doWork and put it into a variable called FirstName. In other words what is on the right hand side of the colon that is defining the variable.

We can also drill into complex objects. Let’s say instead of having twitter as a top level property we have a property called handles and inside of handles we might have several things like Twitter, Facebook and LinkedIn as shown below.


it("can destructure objects",function(){
let doWork = function(){
return{
firstName: "Vivekanand",
lastName: "Rao",
handles:{
twitter: "viveksrao",
linkedin: "vivekanandrao",
facebook:"raosvivek"
}
};
};
let {
firstName: FirstName,
handles:{twitter: TwitterHandle},
handles:{linkedin: LinkedInHandle}
} = doWork();
expect(FirstName).toBe("Vivekanand");
expect(TwitterHandle).toBe("viveksrao");
expect(LinkedInHandle).toBe("vivekanandrao");
});

Now there is a shortcut syntax. If we prefer using a variable name that is the same as the property name that we are trying to retrieve then we don’t have to explicitly specify the variable name. We can just say write the code as shown below.


it("can destructure objects",function(){
let doWork = function(){
return{
firstName: "Vivekanand",
lastName: "Rao",
handles:{
twitter: "viveksrao",
linkedin: "vivekanandrao",
facebook:"raosvivek"
}
};
};
let {
firstName,
handles:{twitter},
handles:{linkedin}
} = doWork();
expect(firstName).toBe("Vivekanand");
expect(twitter).toBe("viveksrao");
expect(linkedin).toBe("vivekanandrao");
});

Object destructuring can be quite effective at deconstructing and pattern matching against objects that are being passed around in a system. It also works when you are defining a function as shown in the below test snippet. In this test we are trying to invoke a function called doWork. It’s being passed a string parameter and a complex parameter that involves data and caching.


it("works with parameters", function(){

let doWork = function(url,{data, cache}){
return data;
};

let result = doWork(
"api/test",{
data: "test",
cache: false
}
);
expect(result).toBe("test");
});

ES2015 – Using const

The const keyword is another addition in ECMAScript 6 (ES6). This one can be a bit confusing depending on the tools you were using only because const has been around not as part of any official ES language specification but the V8 Engine has recognized a const keyword for sometime so you might have seen it used in Chrome or Firefox. But the idea is as the name suggests to create and initialize a read-only variable. A variable that will hold a constant value and something that you can never change. In ES6 const will have block scoping just like the let keyword. Let’s take a look at this in a test.

describe("using const",function(){
"use strict";
it("will make a variable read-only", function(){
const MAX_SIZE = 10;
// MAX_SIZE = 12; // SyntaxError
expect(MAX_SIZE).toBe(10);
});
});

Inside my test, I am using const to declare a variable named MAX_SIZE setting its value to 10. I expect MAX_SIZE to be 10  and off course the above test currently passes.


describe("using const",function(){
"use strict";
it("will make a variable read-only", function(){
const MAX_SIZE = 10;
MAX_SIZE = 12; // SyntaxError
expect(MAX_SIZE).toBe(10);
});
});

However, if I try to assign MAX_SIZE  a value of 12, the test fails. It throws a TypeError: Assignment to constant variable. This represents the semantics of true ES6 const. There will be an error if you try to assign to a const and that’s a little bit different than the const that has been around in browsers like Chrome and Firefox. What they would do is allow you to assign to a const but effectively ignore the value. They wouldn’t throw an error but they also wouldn’t change the value of that variable. In ES6 it is an TypeError.

The following code snippets will demonstrate couple of things. First, const does have block semantics and second how both let and const work when you have multiple variables with the same name.


it("can shadow outer declaration",function(){
var doWork= function(){
var x = 12;
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
});

Currently, the above test has 2 lines of code inside of doWork that declare a variable x and this perfectly legal JavaScript. Running this test will result in a Pass since the value that is returned from doWork is 10. Now what if we change x to a const as done below.

it("can shadow outer declaration",function(){
var doWork= function(){
const x = 12;
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
});

Running the above test, throws an Uncaught SyntaxError: Identifier ‘x’  has already been declared in the browser’s console and that’s another difference between let and const versus var because with let and const we cannot have two variables inside of the same scope that have duplicate names. If const is changed to a let as shown below we get the same error – Uncaught SyntaxError: Identifier ‘x’  has already been declared in the browser’s console.

it("can shadow outer declaration",function(){
var doWork= function(){
let x = 12;
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
});

However, if we move let x = 12 outside of doWork, and run the test, then my test Passes. That’s because the x that is defined inside of doWork is hiding or shadows the x that is defined outside of that function. Inside of doWork I am working with a variable x that holds the value 10.


it("can shadow outer declaration",function(){
let x = 12;
var doWork= function(){
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
});

But outside of doWork I can be working with a variable x that holds the value 12  and so when I am assigning x inside of doWork I am not writing into that outer x.


it("can shadow outer declaration",function(){
let x = 12;
var doWork= function(){
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
expect(x).toBe(12);
});

Changing let to const in the test also passes the test since const x = 12 is defined in a different block, a different scope.


it("can shadow outer declaration",function(){
const x = 12;
var doWork= function(){
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
expect(x).toBe(12);
});

To demonstrate const has a block scoping,

it("can shadow outer declaration",function(){
if(true){const x = 12;}
var doWork= function(){
var x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
expect(x).toBe(12);
});

wrapping const x = 12 in an if block and running the test results in a failure with an error message of ReferenceError: x is not defined.

Finally, the inner x will also work if var was replaced with a let.


it("can shadow outer declaration",function(){
if(true){const x = 12;}
var doWork= function(){
let x = 10;
return x;
};
var result = doWork();
expect(result).toBe(10);
expect(x).toBe(12);
});