let - prevents javascript hoisting to top of document, variables are scoped to function (no more undefined error when trying to call a var outside a functions scope, you get a ReferenceError which is more predictable/expected).
const - provides constants, cannot be redefined, raises syntax error
You can now set default parameters in the function signature. The default value is used even if you explicitly pass in Undefined
example:
let numberOfToppings = function(toppings=[]) {
return toppings.length;
};
console.log(numberOfToppings()); //returns 0
console.log(numberOfToppings(undefined)); //returns 0
console.log(numberOfToppings(['cheese', 'onion'])); //returns 2
Named parameters allow you to define default option properties in the method signature. This makes it easier to reason about how a function should be invoked and what options can be overridden.
example:
let pizzaOrder = function(numberOfPizzas=0, {name, delivery}) {
console.log(numberOfPizzas);
console.log(name);
console.log(delivery);
};
pizzaOrder(2, { name: 'Matt', delivery: false });
//returns 2, 'Matt', false
pizzaOrder(2, {});
//returns 2, undefined, undefined
pizzaOrder(2);
// returns TypeError, we aren't passing the second argument and there isn't a default set.
You can set defaults for named parameters but the syntax isn’t as pretty so I thought I’d showcase the simpler version before this one which has defaults in place to prevent outputting undefined.
let pizzaOrder = function(numberOfPizzas=1, {name, delivery}={name: 'anonymous', delivery: false}) {
console.log(numberOfPizzas);
console.log(name);
console.log(delivery);
};
pizzaOrder(2, { name: 'Matt', delivery: true });
//returns 2, 'Matt', true
pizzaOrder(2);
//returns 2, 'anonymous', false
The for of loop allows you to iterate over property values without having to pass around the index for access.
Example:
let toppings = ['cheese', 'bacon', 'onion']
for(let topping of toppings) {
console.log(topping);
};
//logs cheese, bacon, onion
Rest Parameters allow you to pass an indefinite number of arguments as an array.
Example:
let pizzaToppings = function(...toppings) {
for(let topping of toppings) {
console.log(topping)
};
};
pizzaToppings('cheese');
// returns cheese
pizzaToppings('cheese', 'pepperoni', 'onion', 'tomato', 'bacon');
//returns 'cheese', 'pepperoni', 'onion', 'tomato', 'bacon'
pizzaToppings();
// returns nothing as the for of loop never runs
It's important to note that the rest parameter must be declared last in the function signature.
If you have to call a function that uses rest parameters and you have an array of arguments, you can pass along the array with the spread operator attached.
let toppings = ['cheese', 'onion', 'green pepper']
pizzaToppings(...toppings);
The spread Operator will break up the array into individual arguments.
Arrow function are a way to manage the this keyword. They have a lexical binding and bind to the scope where they are defined and not where they run. Typically use case is a function callback.
example:
let PizzaMaker = function(toppings, cookTime) {
this.toppings = toppings;
this.cookTime = cookTime;
};
let displayConfirmationMessage = function(callback) {
callback();
};
PizzaMaker.prototype.bake = function(){
displayConfirmationMessage(()=> {
console.log(this.toppings);
console.log(this.cookTime);
});
};
let myPizza = new PizzaMaker('cheese', '20mins');
myPizza.bake();
//returns cheese and 20mins.. it has access to the original object properties.
You can use object property shorthand when your property name is the same as your variable name.
Example:
let PizzaMaker = function(toppings, cookTime) {
return { toppings, cookTime };
};
let myPizza = new PizzaMaker('cheese', '20mins');
console.log(myPizza.toppings); //logs cheese
console.log(myPizza.cookTime); //logs 20mins
You can also use this shorthand (object desructuring) to assign local variables from properties. You must use the object property key as the local variable name.
Example:
let PizzaMaker = function(toppings, cookTime) {
let flavoredCrust = true;
return { toppings, cookTime, flavoredCrust };
};
let { toppings, cookTime } = new PizzaMaker('cheese', '20mins');
console.log(toppings); //logs cheese
console.log(cookTime); //logs 20mins
let { flavoredCrust, somethingMadeUp } = new PizzaMaker('cheese', '20mins');
console.log(flavoredCrust); //logs true
console.log(somethingMadeUp); //logs undefined
Template strings are string literals that allow embedded expressions. It's very similar to coffeeScript string interpolation, just the syntax varies. You use backticks, a dollar sign and curly braces.
Example:
let name = "Matt"
console.log(`Hello, my name is ${name}`); //logs Hello, my name is Matt
Template strings also allow for multiline text and keep the newline character.
let letter = `Dear reader,
You'll notice that I set a new line above.
These are preserved in this block of text!`
console.log(letter); //logs
//Dear reader,
//You'll notice that I set a new line above.
//These are preserved in this block of text!
Merges objects together, If familar with jQuery this is the same as $.extend. You can chain as many objects as you like. When duplicate properties exist, the last object properties in the chain will prevail.
Example:
let defaultPizza = {
cheese: true
};
let myPizza = {
cheese: false,
pepperoni: true
};
let mergedPizza = Object.assign({}, defaultPizza, myPizza, { greenPepper: true }, {greenPepper: false});
console.log(defaultPizza); //returns { cheese: true }
console.log(myPizza); //returns { cheese: false, pepperoni: true }
console.log(mergedPizza); //returns { cheese: false, pepperoni: true, greenPepper: false }
You can assign multiple values from an array to local variables
Example:
let toppings = ['cheese', 'pepperoni', 'onion'];
let [topping1, topping2, topping3] = toppings;
console.log(topping1); //logs cheese
console.log(topping2); //logs pepperoni
console.log(topping3); //logs onion
Also, you can skip array items if needed. To do this you represent each index item that you want to skip with a space and a comma.
let toppings = ['cheese', 'pepperoni', 'onion'];
let [ , , topping] = toppings;
console.log(topping); //logs onion
Array.find returns the first element in an array if an element in the array satisfies the provided testing function. Otherwise undefined is returned.
Example:
let toppings = ['cheese','bacon', 'onion'];
let bacon = toppings.find((topping) => {
return topping === 'bacon';
});
console.log(bacon);
// logs bacon
//with objects
let pizzas = [
{ toppings: false, orderID: '1' },
{ toppings: true, orderID: '2' },
{ toppings: true, orderID: '3' },
];
let firstPizzaWithToppings = pizzas.find((pizza) => {
return pizza.toppings;
});
console.log(firstPizzaWithToppings);
//returns { toppings: true, orderID: '2' }
The Map object is a simple key/value object that allows any value (both objects
and primitive values) to be used as either a key or a value. You use the set
method
to add entries and the get
method to read entries. There are other methods such as
clear
to clear the map and has
to check if a map contains a specific key.
Example:
let order1 = { size: 'large', toppings:['cheese'] };
let order2 = { size: 'small', toppings:['cheese'] };
let totalCosts = new Map();
totalCosts.set(order1, '19.99');
totalCosts.set(order2, '9.99');
console.log(totalCosts.get(order1)); // logs 19.99
console.log(totalCosts.get(order2)); // logs 9.99
console.log(totalCosts.has('my made up key')); //logs false
totalCosts.clear();
console.log(totalCosts.has(order1)); //logs false
You can also iterate maps with a for..of loop.
let orders = new Map();
orders.set('order-1', '19.99');
orders.set('order-2', '9.99');
for(let [key, value] of orders) {
console.log(`${key} : ${value}`);
};
//returns order-1 : 19.99, order-2 : 9.99
The Set object lets you store unique values of any type, whether primitive values or object references. You can also iterate over sets with for..of.
Example:
let toppings = new Set();
toppings.add('cheese');
toppings.add('pepperoni');
toppings.add('bacon');
toppings.add('pepperoni');
for(let topping of toppings) {
console.log(topping)
};
//returns cheese, pepperoni, bacon
//The last add('pepperoni') was ignored as the value was a duplicate
JavaScript classes are syntactical sugar over JavaScript's existing prototype-based inheritance. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance. Your object properties are typically setup with the constructor methosd. There can only be one special method with the name "constructor" in a class.
Example:
class Pizza {
constructor(size, toppings) {
this.size = size;
this.toppings = toppings;
}
bake() {
return `cooking a ${this.size} pizza.`;
}
}
let myPizza = new Pizza('large', ['cheese', 'bacon']);
console.log(myPizza.bake()); // returns cooking a large pizza.
The extends keyword is used in class declarations or class expressions to create a class with a child of another class.
Example:
class PizzaBase {
constructor(size, toppings=[]) {
this.size = size;
this.toppings = toppings;
}
bake() {
return `cooking a ${this.size} pizza.`;
}
}
class MeatLoversPizza extends PizzaBase {
constructor(size) {
super(size);
this.toppings = ['bacon', 'pepperoni', 'sausage'];
}
}
let myPizza = new MeatLoversPizza('large');
console.log(myPizza.toppings); // returns bacon, pepperoni, sausage
console.log(myPizza.bake()); // returns cooking a large pizza.
Modules provide a way to share methods throughout your app without polluting the global namespace. A module is an anonymous function that is exposed via the export keyword. When declared this way other pieces of code can use these functions via the import keyword.
Example:
//lets pretend this function lives in a file named logger.js
export default function(message) {
console.log(message)
}
//now lets pretend we want to use this method in a file named pizza.js.
//We can import the method like so:
import myLogger from './logger';
//now we have the logger function exposed in our pizza.js and can use it like so:
myLogger('hello');
//Note, since I used 'default' when declaring the export I can name it anything I
//wish when importing... myLogger could have been myPizzaLoggerYum.
You can also exports multiple functions with a slightly different syntax.
//lets pretend these function lives in a file named logger.js
function consoleLogger(message) {
console.log(message);
}
function alertLogger(message) {
alert(message);
}
exports { consoleLogger, alertLogger}
//now when importing you have to use the exact name declared in the export.
import { consoleLogger, alertLogger} from './logger';
A nice use case for modules is constants.
//lets pretend we are in a file named constants.js we can export constants like so:
const MIN_TOPPINGS = 1;
const MAX_TOPPINGS = 12;
exports { MIN_TOPPINGS, MAX_TOPPINGS }
//now that these are defined via export, we can import into any file via import.
import { MIN_TOPPINGS, MAX_TOPPINGS } from './constants';
The Promise object is used for deferred and asynchronous computations. A Promise
represents an operation that hasn't completed yet, but is expected in the future.
The promise constructor takes in an anonymous function with two callback arguments,
one argument for resolving and another for rejecting. Once a promise is fulfilled we
can call then
on it to read the results from the promise. If the promise is rejected
then the execution moves immediately to the catch
function.
Example:
let getOrderStatus = function(orderId) {
return new Promise(resolve, reject) {
let url = `/order/status/${orderId}`;
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.onload = function() {
resolve(JSON.parse(request.response));
};
request.onerror = function() {
reject(new Error('could not find order'));
};
}
};
let orderStatus = getOrderStatus(1);
orderStatus.then(function(result){
console.log(result);
});
orderStatus.catch(function(error) {
console.log(error);
});
A generator object is a way to create iterator objects. Once you have an interator object you can use for..of loops, the spread operator and destructuring assignments.
Example:
function * toppings(){
yield 'cheese';
yield 'pepperoni';
}
for(let topping of toppings()){
console.log(topping);
}// logs cheese, pepperoni
console.log([...toppings()]);// logs ['cheese','pepperoni']
let [topping1, topping2] = toppings();
console.log(topping1);// logs cheese
console.log(topping2);// logs pepperoni