Browser support
ES2015 was a big update to JS. To see a comprehensive list of support for ES2015 see https://kangax.github.io/compat-table/es6/
Because ES2015 isn’t fully supported it should be transpiled to ES5. Babel is the most popular transpiler. For small projects you can link to a Babel transpiler and transpile in the browser. However, this is slow and not recommended for working on large projects or for production.
const
and let
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
const
and let
allow you to define block scoped variables. Scope hoisting allows you to assign a variable before it is declared. But you cannot do this with const
a and let
because of the temporal dead zone. While var
is hoisted to the top of the scope, let
and const
are scoped hoisted to the nearest block.
x = 5; // assignment
console.log(x); //using it
var x; // declaring it
y = 5; // assignment
console.log(y); //using it
let y; // declaring it
let z = 15;
console.log(z);
if(z > 12) { let z = 5; } // the value of z isn't changed globally.
console.log(z);
As you can see let
allows you to declare variables that are limited to the statement, block, or expression.
Const creates a read-only reference to a variable but it’s not immutable.
const dozen = 12;
const dozen = 13; // will not work
if (dozen === 12) {
let dozen = 13; // this will work because JavaScript is block scoped
console.log(dozen);
}
console.log(dozen); // dozen is still 12
Template literals
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
These are also called template strings or string literals.
const name = "Brad";
const thing = "dog";
const sentance = `I am a ${thing}, my name is ${name}`;
Template literals preserve white space and line breaks. They also allow you to include expressions.
Tagged Template Literals
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
function tagTransformer(strings, ...values) {
let res = "";
values.forEach((val, i ) => { values[i] = val.toUpperCase()});
res = strings.reduce((acc, val, i) => {
return acc + values[i - 1] + val;
})
return res + "!";
}
let description = "great";
let kind = "chocolate chip";
let transofrmed = tagTransformer`This is a ${kind} cookie, and it is ${description}`;
console.log(transofrmed);
Arrow functions
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/
let add = (a) => { return (b) => (a + b) };
let add5 = add(5);
console.log(add5(15) === 20);
Arrow functions do more than provide a cleaner syntax. They also help work around the problem of this
. Arrow functions don’t have their own this
. They inherit this
from the enclosing scope. However, they may complicate debugging since they have an anonymous stack trace. Arrow functions bind to the scope of where they are defined, not where they are called. This is called lexical binding.
Rest and Spread Operators
Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
Below the array is spread as individual arguments in the function call. But in the function definition, all of those arguments are collected into an array.
function foo(x, ...argz) {
console.log(`The first argument is: ${x}`);
argz.forEach((x) => {console.log(x)});
}
let argz = [1,2,3,4,5,6,7];
foo(...argz);
Default Arguments
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
function error(m) {
console.log(m)
}
function foo(arg = error(`You seem to have forgotten something.`)) {
console.log(`So glad you remebered the arg: ${arg} `);
}
foo();
foo({a: 36});
function bar(options) {
options = Object.assign({ size: 'large', color: 'blue', speed: 'fast' }, options)
for (let option in options) {
console.log(options[option])
}
}
bar({ size: 'medium' });
Object Initializer Shorthand
When the variable has the same name as the object property you can use shorthand syntax.
const name = 'foo', age = 25;
const obj = {name, age};
Object Destructuring
Mozilla Destructuring Assignment
Object destructuring is one of my favorite features of ES2015.
function foo(options = {size: 'large', color: 'blue', speed: 'fast'}) {
let { size, color } = options ;
console.log(size, color)
}
foo();
function bar({ a = 4, b = 5, c = 6 }) {
console.log(a, b, c);
}
bar({
a: 1,
b: 2,
c: 3
});
bar({
a: 1,
c: 3
});
bar({});
Concise Properties and Concise Methods
let foo = "24";
var obj = {
b() {},
foo,
}
Computed Properties and Computed Methods
let foo = "24";
let bar = "foo";
var obj = {
["my" + bar.toUpperCase()]() { console.log(this)},
[bar.toUpperCase()]: 46,
}
console.log(obj.FOO);
obj.myFOO();
Symbols
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Symbols are a new data type. Symbols can be created and used as object properties. The most useful symbols are the built in symbols. They can be used to change the default behavior of certain operations.
- Symbol.iterator
- Symbol.toStringTag
- Symbol.toPrimitive
- Symbol.isConcatSpreadable
let a = [1,2,3];
let it = a[Symbol.iterator]();
let x = it.next();
while (x.done === false) {
console.log(x);
x = it.next();
}
let b = {
*[Symbol.iterator]() {
for (let k in this ) {
yield this[k];
}
},
1: 1,
2: 2,
3: 3,
}
let a = [ ...b ];
console.log(a);
For Of Loop
The for...of
loop can iterate over any iterable.
let a = [1, 2, 3, 4, 5];
for (let v of a ) {
console.log(v);
}
Generators
Generating iterators.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator
let animals = {
monkey: 1,
horses: 3,
cows: 4,
sheep: 10,
moose: 3
};
function* gen(obj) {
for (let k in obj ) {
yield `${obj[k]} ${k}`;
}
}
let it = gen(animals);
let x = it.next()
while(x.value) {
x = it.next();
console.log(x)
}
Array.find()
Returns the first matching element in an array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
let a = [..."isastringanarray"].find((el) => { return el === "a"});
console.log(a);
Object.Assign
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
The Object.assign method copies properties from one or more source objects to a target object specified as the very first argument. This will take two objects and merge them in a 3rd object. Duplicate values from the rightmost object will override those to the left. You can have more than two objects.
let odd = {
one: 1,
thee: 3,
five: 5
}
let even = {
two: 2,
four: 4,
six: 6
}
let count = Object.assign({}, odd, even);
console.log(count);
Maps
Use Maps when the keys are unknown until runtime. Use Maps when all keys are of the same type and all values are of the same type. If there are mixed types use Objects. Maps are iterable.
- The keys of an
Object
areStrings
andSymbols
, whereas they can be any value for aMap
, including functions, objects, and any primitive.- The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.
- You can get the size of a
Map
easily with thesize
property, while the number of properties in anObject
must be determined manually.- A
Map
is an iterable and can thus be directly iterated, whereas iterating over anObject
requires obtaining its keys in some fashion and iterating over them.- An
Object
has a prototype, so there are default keys in the map that could collide with your keys if you’re not careful. As of ES5 this can be bypassed by usingmap = Object.create(null)
, but this is seldom done.- A
Map
may perform better in scenarios involving frequent addition and removal of key pairs.
let count = {
one: 1,
two: 2,
thee: 3,
four: 4,
five: 5,
six: 6
};
let map = new Map();
for (let k in count) {
map.set(count[k], k);
}
console.log(count);
console.log(map);
WeakMaps
A WeakMaps keys must be objects, the values can be anything.
One difference to
Map
objects is thatWeakMap
keys are not enumerable (i.e., there is no method giving you a list of the keys). If they were, the list would depend on the state of garbage collection, introducing non-determinism.
This makes WeakMaps useful for protecting private information.
http://fitzgeraldnick.com/2014/01/13/hiding-implementation-details-with-e6-weakmaps.html
WeakMap keys may be garbage collected, also the values the keys reference may be garbage collected as well. This make them memory efficient.
Sets
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
If a map is comparable to an object, then a set is comparable to an array. However, a value in a set must be unique.
let count = {
one: 1,
two: 2,
thee: 3,
four: 4,
five: 5,
six: 6
};
let set = new Set();
for (let k in count) {
set.add(k);
}
set.delete("four");
console.log([...set.keys()]);
console.log(set);
WeakSets
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet
WeakSets are similar to week maps, they only store objects. The objects are garbage collected. You cannot enumerate a WeakSet. A week set has three methods, add
, set
, and delete
.
Classes
Classes are syntactic sugar over JavaScript’s prototypal inheritance.
The
static
keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.
When a class extends another class.
If there is a constructor present in the subclass, it needs to first call super() before using “this”.
class Store {
constructor(name, address) {
this.name = name;
this._address = address.toUpperCase();
}
get address() {
return this._address;
}
set address(address) {
this._address = address.toUpperCase();
}
}
class Bakery extends Store {
constructor(name, address, specialty) {
super(name, address );
this._specialty = specialty;
}
get specialty() {
return this._specialty
}
}
let goldenBuns = new Bakery("Golden Buns", "123 Wheat St", "Bread");
console.log(goldenBuns.address);
console.log(goldenBuns.specialty);
Promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
JavaScript is single threaded and thus synchronous by nature. Promises provide a mechanism for writing asynchronous code.
let url =
"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise";
function requestURL(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.response);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
requestURL(url)
.then(results => {
document.body.innerHTML = results;
})
.then(() => {
console.log("Then the page was updated.");
})
.catch(error => {
console.log(`Error: ${error}`);
})
.finally(() => {
console.log("Finally, it's over.");
});
Modules
The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the
import
statement.Exported modules are in
strict mode
whether you declare them as such or not. The export statement cannot be used in embedded scripts.
You cannot export default, variables defined with var
, let
, or const
.
export default myFunction;
import myFunction from './myJsFile.js'
There are other module syntaxes in JavaScript. Node.js uses common.js modules.
const myFunction = require('myfunction');
You can read more about various module syntaxes here: ES6 Modules