String
Template
Literals
You can now declare strings with `back ticks` (grave accent). These are special strings that allow for interpolation that is a lot cleaner that old-school string concatenation.
var firstName = "Jìan";
var lastName = "Yáng";
var title = "web developer";
// ughhh, so much concatenation...
var old = "Hi, my name is " + firstName + " " + lastName + ", and I am a " + title + ".";
// much better
var new = `Hi, my name is ${firstName} ${lastName}, and I am an ${title}.`;
Once you're in the ${ } block, you're back in regular JavaScript, and any expression you put in gets evaluated.
function addTwo(x, y) {
return x + y;
}
var j = 99;
var k = 42;
var numberyString = `Adding ${j} and ${k} together results in ${addTwo(j, k)}.`;
Essentially anything in the ${ } will be given the .toString() treatment, so be careful with interpolating objects.
var character = { name: 'Thor', age: 1000 };
var str = `Favorite Character: ${character}`;
// 'Favorite Character: [object Object]'
// Much better if we use JSON.stringify
str = `Favorite Character: ${JSON.stringify(character)}`;
// 'Favorite Character: {"name":"Thor","age":1000}'
let
and
const
const
The const keyword lets you prevent a variable from ever being re-declared or re-assigned.
const PI = 3.14;
/*
bunch of code here
*/
PI = 15;
// TypeError: Assignment to constant variable.
let
The let keyword creates a block-scoped variable.
for (var i = 1; i < 4; i++) {
console.log(i);
}
// 1
// 2
// 3
console.log(i);
// 4
for (let i = 1; i < 4; i++) {
console.log(i);
}
// 1
// 2
// 3
console.log(i);
// ReferenceError: i is not defined
It can be re-assigned but not re-declared (unlike var).
let z = 5;
z = 25;
let z = 10;
// SyntaxError: Identifier 'z' has already been declared
What is a code block?
{
// this is a code block
let x = 5;
var y = 10;
const z = -Infinity;
}
console.log(x);
// ReferenceError: x is not defined
console.log(y);
// 10
console.log(z);
// ReferenceError: z is not defined
Essentially any pair of curly braces (outside of object syntax).
Surprise!
const is also block-scoped
You'll mostly use code blocks in for loops and if statements.
if (x > 10) {
let happy = true;
// happy lives in this code block forever
}
console.log(happy);
// JavaScript's like: "never heard of 'em"
They're still there even if you omit the curlies
if (x > 10) let happy = false;
console.log(happy); // "JavaScript: 'who??'
Keyword | Can Be Re-Assigned | Can Be Re-Declared | Scope Rules |
---|---|---|---|
var | yes | yes | default (function scope only) |
let | yes | no | block scope |
const | no | no | block scope |
Default
Parameters
If you have parameters that might be undefined (i.e. the user didn't pass them), you can easily assign default values in ES6
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2));
// 10
console.log(multiply(5));
// 5
Note: these only work for undefined values, not other falsy values.
Arrow
Functions
=>
[1,2,3].forEach(function(el, idx) {
console.log(el, idx);
});
/* is the same as */
[1,2,3].forEach((el, idx) => {
console.log(el, idx);
});
They cannot be named functions and they only work as function expressions. Ideal for shortening callbacks.
[1,2,3,4,5].filter(function(el, idx) {
return el % 2 === 0;
});
/* is the same as */
[1,2,3,4,5].filter((el) => {
return el % 2 === 0;
});
/* square everything */
const arr = [1,2,3];
const arrSquared = arr.map(el => el ** 2);
// [1, 4, 9]
Just 1 argument?
Param parentheses are optional
const myArrowFunc = () => {
console.log(this);
// window! Inherited lexically
};
Unlike normal functions declared with the function keyword, arrow functions have no this context. Instead their this follows lexical scoping rules.
Rest / Spread
Operator
function sumMany(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
sumMany(5, 10); // 15
sumMany(10, 10, 10, 10, 10, 10, 10, 10, 10); // 90
sumMany(1); // 1
aka the coolest feature of ES6
In any function declaration, the ...nums collects any number of additional arguments you pass to a function into a single nums array.
function oneOrMoreArguments(first, ...more) {
console.log(first);
more.forEach(arg => {
console.log(arg);
});
}
You can also define a couple of params and collect the rest
In this instance, first is always going to be there while more will be an array of everything else you passed in.
function takesFour(one, two, three, four) {
console.log(one);
console.log(two);
console.log(three);
console.log(four);
}
const names = ['Elie', 'Matt', 'Michael', 'Joel'];
takesFour(...names);
// Elie
// Matt
// Michael
// Joel
The same '...' syntax can also be used in a different context as the spread operator. When calling a function for instance, you can spread out array elements:
const whiskey = {
name: 'Whiskey',
species: 'canine',
cool: true
};
const anotherDog = { ...whiskey, name: 'Gandalf' };
/*
{
name: 'Gandalf',
species: 'canine',
cool: true
}
*/
Or if you need to copy over pieces of an object!
'name' has to come after ...whiskey here. Why?
Object
and
Array
Destructuring
But that's like waaay too much typing / boring and redundant
let userData = {
username: 'hueter',
id: 12345,
password: 'fiddlesticks',
firstName: 'Michael',
lastName: 'Hueter',
age: 'guess',
isLegit: undefined
};
let username = userData.username;
let firstName = userData.firstName;
let lastName = userData.lastName;
let id = userData.id;
JavaScript programmers take things out of objects all the time.
So we came up with some syntactic sugar.
let userData = {
username: 'hueter',
id: 12345,
password: 'fiddlesticks',
firstName: 'Michael',
lastName: 'Hueter',
age: 'guess',
isLegit: undefined
};
let { username, firstName, lastName, id } = userData;
console.log(username); // hueter
console.log(id); // 12345
You can also rename things
const userData = {
username: 'hueter',
id: 12345,
password: 'fiddlesticks',
firstName: 'Michael',
lastName: 'Hueter',
age: 'guess',
isLegit: undefined
};
const { username: newName } = userData;
console.log(newName); // hueter
const userData = {
username: 'hueter',
id: 12345,
password: 'fiddlesticks',
firstName: 'Michael',
lastName: 'Hueter',
age: 'guess',
isLegit: undefined
};
const { password, ...user } = userData;
console.log(user);
/*
{
username: 'hueter',
id: 12345,
firstName: 'Michael',
lastName: 'Hueter',
age: 'guess',
isLegit: undefined
}
*/
You can apply the same concept to arrays!
const myFavoriteThings = ['teaching', 'music', 'hiking', 'dank memes'];
const [first, second, ...others] = myFavoriteThings;
console.log(first); // 'teaching'
console.log(second); // 'music'
console.log(others); // ['hiking', 'dank memes']
Array.from()
Sometimes JavaScript gives you array-like objects that you wish were arrays
const paragraphsNodeList = document.querySelectorAll('p');
Array.isArray(paragraphsNodeList); // false
const paragraphs = Array.from(paragraphsNodeList);
Array.isArray(paragraphs); // true
Now you can easily make an Array out of those things with Array.from!
Array.from() under the hood
const cat = 'cat';
Array.from(cat);
// ['c', 'a', 't']
Basically it works by looking for anything with a .length property. Then it iterates over the items and pushes them into an array.
You can use this trick to build empty arrays of a certain size:
Array.from({ length: 5 });
// [undefined, undefined, undefined, undefined, undefined]