Explore New Syntax in JavaScript ES6
Learning Objectives
After completing this unit, you'll be able to:
- Describe the difference between function and block scoping.
- State a reason why the const keyword should be used.
- Recognize shorthand ES6+ syntax used to initialize variables.
- Identify the new destructuring syntax used to separate data.
- Identify the backtick character used to create template literals.
Function vs Block Scope
Before ES6, the only way you could declare a variable or a function in JavaScript was using the var
keyword. You now have other options, and in this unit we go over why you want to use them.
To begin, let's look a little closer at how variables are defined. Variables declared with the var
keyword are said to be in the function scope. This means that a variable would exist only within the scope of the function in which it was declared. Or the nearest parent function, if it's a nested function.
That makes sense. And it still makes sense when you consider global scope, in which a variable is declared outside of a function. Take, for example, the following code.
var myVar = 1; function myFunc() { var myVar = 2; console.log(myVar); } myFunc(); console.log(myVar);
What numbers do you think you'd see in the console? 1 or 2?
You should see both. First 2 and then 1. Because of function scope, 2 is printed when the function is called. And because of global scope, 1 is displayed the second time, when the variable is written to the log outside the function.
Most developers get this, and everything is great until they encounter code containing an if statement, such as this example.
var myVar = 1; if (true) { var myVar = 2; console.log(myVar); } console.log(myVar);
Now, what do you think will appear in the console? The same thing?
Nope. Executing that code results in the number 2 appearing twice, because the fact that the var
keyword does not support block scope.
If you're a developer coming from a different language, such as C++, C#, or Java, you may have learned a little something about block scope, and the last code example makes no sense to you. Don't worry, you're not alone—it has confused a lot of people.
A block is any code within curly braces. Block scoping ensures that any variables defined within those braces don't become global variables. They instead have local scope. Having this type of control over how variables are scoped helps you prevent unexpected behaviour in your code.
Let Is the New Var
The lack of block scoping has caused many headaches for JavaScript developers, especially when it comes to variables declared in for loops. ES6 aimed to do away with all this unnecessary hardship by introducing the let
keyword.
Variables assigned with let
are always block scoped. But this isn't the only benefit to using the let
keyword. Variables assigned this way also cannot be hoisted.
Hoisting occurs when the JavaScript interpreter makes two passes at your code. In the first pass, variable and function declarations are “hoisted” to the top of the code. And in the second pass they are evaluated and assignments are made. If only we had a nickel for every time that poorly understood behavior has caused a bug.
You might be thinking, “If the let keyword is so great and avoids all those confusing bugs, why would anyone ever want to use var?”
We hear you. For this reason, many JavaScript developers now use let
almost exclusively. And we suggest you do too. Using block-scoped variables is not only less error-prone, but makes it easier for other developers to know how a variable was meant to be scoped.
And There's Also Const
ES6 also introduced another keyword, const
. You can probably guess what type of variable this defines—yep, it's a constant.
This can be useful when you need to declare a variable that cannot be redeclared or reassigned. Essentially, it's read-only. If you declare a variable as a constant and then try to reassign it later in the code, it throws a type error. Variables declared with the const
keyword are also block-scoped and cannot be hoisted, which you already learned is a good thing.
However, there are a couple of things to be aware of when using the const
keyword. Since const
values cannot be reassigned, they must be initialized at the time they are declared. If you tried to execute the following code, you would get an error telling you either unexpected token or that it was missing an initializer in the const
declaration.
const BRANDCOLOR; console.log(BRANDCOLOR);
And here is something that tends to trip up developers: Constants are not immutable. This means that it is possible to modify the properties of objects or arrays assigned with const
. For example, if you declare an object such as this:
const BRANDCOLOR = { primary: "blue", accent: "teal" }
You can execute the following code later on and the accent property will be reassigned without throwing an error.
BRANDCOLOR.accent = 'gray'; console.log(BRANDCOLOR);
The bottom line of all of this is that you definitely should use const
to define variables whose values will never change. There are also some JavaScript developers who think that you should always use const
before let
(unless you know that the variable's value will change). That decision is a matter of preference.
Just don't forget that when dealing with objects or arrays, only the object itself cannot be reassigned. Properties within that object or array can be changed.
Why Type the Same Thing Twice?
JavaScript developers are always trying to get data in and out of arrays or objects. More than likely, you have come across code like this, in which the properties of an object are initialized using variables:
let firstName = 'John', lastName = 'Doe'; let user = { firstName : firstName, lastName : lastName } console.log(user);
In ES6, you no longer have to repeat yourself, as long as the variables and object property names are the same. This code accomplishes the same thing:
let firstName = 'John', lastName = 'Doe'; let user = { firstName, lastName }; console.log(user);
All we did here was remove the repeating variable names and colons ( :
). This shorthand comes in handy when you have objects containing a large number of fields.
But that's not all. ES6 provides a simpler way of getting data out of arrays or objects. This also helps reduce repetitive lines of code. Take for example an array that contains four numbers:
let numbers = [1, 2, 3, 4];
To get data out of this array, you can assign its values to variables like this:
let one = numbers[0], two = numbers[1], three = numbers[2], four = numbers[3]; console.log(one);
You can now access the data through the variable names. So here the number 1 would be printed to the console. Even though this works, you might prefer to use a shortened method known as array destructuring.
let [one, two, three, four] = numbers; console.log(one);
The brackets on the left side of the assignment are part of the new destructuring syntax. So, this code is the same thing as saying, “Give me four variables named one, two, three, and four, and assign the first value in the numbers array to variable one, the second value to variable two, and so on.” Shorter, sweeter, great.
But now let's say that you want to get just the third value, and you are not really interested in assigning variables to the first two elements. You can still use this syntax:
let [ , , three] = numbers;
We just used commas as placeholders for the first two elements, so variables aren't created behind the scenes, and everything after the third position is ignored.
It pretty much works the same way for objects, but instead of square brackets, you use curly braces. Consider this object:
const APPLE = { type: 'red delicious', color: 'red', size: 'large' }
If you want to get data for the type and color properties and then print out the color variable, you can do this.
const { type, color } = APPLE; console.log(color);
One thing to keep in mind is that if you try to use the destructuring syntax with a variable name that does not exist, you get back “undefined.” For example, if you try to do the following, you'll see undefined in the console.
const { type, color, size, packaging } = APPLE; console.log(packaging);
A Better Error Message
If you've ever worked with string interpolation in another language, then you were probably terribly frustrated the first time you had to generate an error message using JavaScript. Up until ES6, the most common way to create strings that contained variables or expressions was to use standard string concatenation. You know, the old addition ( +
) operator.
ES6 introduced template literals, along with the backtick ( `
) character. Template literals allow you to easily embed expressions within your string using an expression notation that should look pretty familiar. For example, this code can be used to generate the message, “The following 'user', Me has encountered an "error".”
let user = 'Me'; console.log(`The following 'user', ${user} has encountered an "error".`);
Template literals offer many advantages, such as:
- The ability to embed single and double quotation marks without using escape characters.
- Multiline messages, which are great when you need to create a string that contains HTML markup spanning multiple lines.
- Tagged templates, which allow you to run the template through a function that you create. This gives you greater control over what the resulting string looks like.
We think that once you start working with template literals, you'll come to love them as much as we do.
Resources
- MDN Web Docs: Block scoping defined in the MDN web docs
- MDN Web Docs: Object.assign() method
- W3Schools: JavaScript Hoisting
- MDN Web Docs: Let keyword
- MDN Web Docs: Const keyword
- MDN Web Docs: Object Initializer
- MDN Web Docs: Destructuring Assignment
- MDN Web Docs: Template Literals