Table of Contents generated with DocToc
- FrontEnd Masters - ES6 notes
- ECMAScript is now EcmaScript. Which is a standard for the API JavaScript and other languages use.
- TC39 stands for Technical Committee which regulate the EcmaScript API.
- ES.Next is a pointer to the next version of ES
- ES Harmony is the backlog of the new stuff coming to ES and the versions in development.
David Herman
Proper Tail Call (PTC) allows recursive calls without flooding the memory usage with garbage. The current limit of recursive calls is around 10k in Chrome and 49k in FF.
ES6 brings proper tail calls.
Tail position = the last instruction to fire before the return statement Tail call = calling another function from the tail position Close call = when the last instruction has to return to the method to do something. eg. return 1 + bar()
Only works on Strict Mode
// Function Declaration
function foo() {
// code here
}
// Function Expression
var bar = function() {
// code here
}
Function declaration gets hoisted to the top, while Function Expression does not.
- var: gets hoisted
- let: lives within block (curly braces)
- const: constant.. also lives within blocks
function doSomething() {
console.log(a); // should cause an error
let a = 1;
console.log(a);
}
Treats arguments as an array
function foo(...bar) {
console.log(bar.join(' ')); // Logs 'I can haz teh arguments'
}
foo('I', 'can', 'haz', 'teh', 'arguments');
- It is similar to
arguments
but the rest params are a real array. - You just can have one rest param per function and has to be in the last position.
- You can't use arguments
Spreads an array into its individual values.
var a = [1, 2];
var b = returnTwo(a[0], a[1]); // [2, 1]
var c = returnTwo(...a); // [2, 1]
let nums = [1, 2, 3];
let abcs = ['a', 'b', 'c'];
let alphanum = [ ...nums, ...abs ]; // [1, 2, 3, 'a', 'b', 'c']
const x = 4;
const y = 2;
const o = { x, y, z: x * y }; // { x: 4, y: 2, z: 8 }
"Destructuring allows you to bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable."
It helps pull incoming objects apart.
var address = {
city: "Costa Mesa",
state: "CA",
zip: 92444
};
let {city, state, zip} = address;
log(city); // 'Costa Mesa'
log(state); // 'CA'
log(zip); // 92442
or we can use alias
var address = {
city: "Costa Mesa",
state: "CA",
zip: 92444
};
let {city: c, state: s, zip: z} = address;
log(c, s, z); // 'Costa Mesa CA 92444'
You can also use it like
var person = {name: 'Aaron', age: 35};
displayPerson(person);
function displayPerson({name, age}) {
// do something with name and age to display them
}
You can pass default values
var person = {name: 'Aaron', age: 35};
displayPerson(person);
function displayPerson({name = "No Name provided", age = 0}) {
// do something with name and age to display them
}
The destructuring must match the object or else it will throw an error.
var person = {name: 'Aaron', age: 35};
let {name, age, address} = person; // throws! (irrefutable)
let {name, age, ?address} = person; // is ok because we specified address as undefineable (refutable)
let ?{name, age, address} = person; // Forgives the whole pattern
let {a: x} = {} // throw
let ?{a: x} = {} // x = undefined
let ?{a: x} = 0 // x = undefined
let {?a: x} = {} // x = undefined
let {?a: x} = 0 // throw
let ?{a: x = 1} = undefined // x = 1
let {?a: x = 1} = undefined // throw
let {?a: x = 1} = {} // x = 1
let person = {
name: "Aaron",
age: "35",
address: {
city: "Salt Lake City",
state: "UT",
zip: 84115
}
};
let {name, age, address: {city, state, zip}} = person; // this won't create address, but will create city, state, zip
var nums = [1, 2, 3, 4, 5];
var [first, second,,,,fifth] = nums;
log(first, second, fifth); // 1, 2, 5
how to swap variables without using a temp var
var a = 1, b = 2;
// The Old Way
var temp = a, a = b, b = tmep;
// The New Way
[b, a] = [a, b];
var nums = [1, 2, 3, 4];
doSomething(nums);
function doSomething([first, second, ...others]){
log(first); //logs 1
log(second); //logs 2
log(others); //logs [3, 4]
}
var nums = [1, 2, [30, 40, [500, 600]]];
var [one,,[thirty,,[,sixhundert]]] = nums;
let [x] = [2, 3] // x = 2
let [x] = {'0': 4} // x = 4
let [x, y, z] = [1, 2] // throw
// Entire Pattern is Refutable
let ?[x, y, z] = [1, 2] // x = 1, y = 2, z = undefined
// Only 'z' is Refutable
let [x, y, ?z] = [1, 2] // z = 1, y = 2, z = undefined
They can't be use with new
because of how they bind this
.
var fn1 = function() {return 2;};
var fn2 = () => 2; // Here you can omit curly braces. It means return 2. If you add curly braces then you have to put the word 'return'.
var x;
x = () => {}; // No parameters, MUST HAVE PARENS
x = (val) => {}; // One parameter w/ parens, OPTIONAL
x = val => {}; // One parameter w/o parens, OPTIONAL
x = (y, z) => {}; // Two or more parameters, MUST HAVE PARENS
x = y, z => {}; // Syntax Error: must wrap with parens when using multiple params
You don't need to bind(this) or var _this = this.
var widget = {
init: function() {
document.addEventListener("click", (event) => {
this.doSomething(event.type);
}, false);
},
doSomething: function(type) {
console.log("Handling " + type + " event");
}
};
Widget.init();
You can't replace all functions with Arrow functions because it will mess up this
.
var monsterHealth = Symbol(); // Symbol() is a JS method that acts like a GUID generator
var monsterSpeed = Symbol();
class Monster {
constructor(name, health, speed) {
this.name = name;
this[monsterHealth] = health;
this[monsterSpeed] = speed;
}
// getter
get isAlive() {
return this[monsterHealth] > 0;
}
// setter
set isAlive(alive) {
if(!alive) this[monsterHealth] = 0;
}
// method
attack(target) {
console.log(this.name + ' attacks ' + target.name);
}
}
var Jorge = new Monster('Jorge', 3);
Jorge.isAlive; // true
jorge.isAlive = false;
console.log(jorge.isAlive); // false
The following will fall in a cyclical death trap because the setter for name is already in the constructor.
class Monster {
constructor(name) {
this.name = name;
}
// setter
set name (name) {
this.name = name;
}
}
var Jorge = new Monster('Jorge', 3);
jorge.name = 'kevin';
Classes don't hoist.
class Godzilla extends Monster {
constructor() {
super('Godzilla', 10000);
}
attack(target) {
super(target); // will call the Monster attack method
}
}
SETs are similar to Arrays. The difference is they force unique values. No typecasting in keys.
var set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.size; // logs 3. It is like Array.prototype.length
set.has(2); // true
set.clear(); // deletes all values
set.delete(2); // deletes value 2
Another way to create a Set
var set = new Set([1, 2, 3, 5]);
A new loop
var set = new Set([1, 2, 3, 5]);
for (let num of set) {
console.log(num); // logs 1, 2, 3, 5
}
No typecasting in keys.
var map = new Map();
map.set('name', 'Jorge');
map.get('name'); // Jorge
map.has('name'); // true
The key can be a function, a primitive, an object.. But it has to be exactly the same. If it is a copy or it is mutated, then it will stop working.
var user = { name: 'Jorge', id: 1234 };
var userHobbyMap = new Map();
userHobbyMap.set(user, ['Ice Fishing', 'Family Outting']);
Like a map but it doesn't has a size and no primitive keys.
It will not hold to a key that is not used by any other element. This is useful to prevent unlimited garbage. eg. when using a DOM element as a key in a map, then the DOM element gets deleted, the weakmap will delete that key-value as well.
A weakmap holds only a weak reference to a key, which means the reference inside of the weakmap doesn't prevent garbage collection of that object.
Like CommonJS
The default means will import the default export.
// MyClass.js
class MyClass{
constructor() {}
}
export default MyClass;
// Main.js
import MyClass from 'MyClass';
You can call just the exports you need from a specific module.
// lib.js
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
// main.js
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
// second.js
// or you can call them with '*'
// but then you have to prefix the exports with
// the module name
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
// lib.js
class MyClass {
//...
}
// main.js
import { Dude as Bro } from 'lib';
var bro = new Bro(); // instanceof MyClass
The following would be allowed
// lib.js
import Main from 'main';
var lib = {message: "This Is A Lib"};
export { lib as Lib };
// main.js
import { Lib } from 'lib';
export default class Main {
// ....
}
// lib.js
// Default exports and named exports
import theDefault, { named1, named2 } from 'src/mylib';
import theDefault from 'src/mylib';
import { named1, named2 } from 'src/mylib';
// Renaming: import named1 as myNamed1
import { named1 as myNamed1, named2 } from 'src/mylib';
// Importing the module as an object
// (with one property per named export)
import * as mylib from 'src/mylib';
// Only load the module, don't import anything
import 'src/mylib';
export var myVar = ...;
export let myVar = ...;
export const MY_CONST = ...;
export function myFunc() {
...
}
export function* myGeneratorFunc() {
...
}
export class MyClass {
...
}
This is for exporting something you are importing.
export * from 'src/other_module';
export { foo, bar } form 'src/other_module';
// Export other_module's foo as myFoo
export { foo as myFoo, bar } from 'src/other_module';
This method will return a promise
System.import('some_module')
.then(some_module => {
...
})
.catch(error => {
...
});
Promise.all(
['module1', 'module2', 'module3']
.map(x => System.import(x)))
.then(function ([module1, module2, module3]) {
// my code...
});
System.import(source);
// Returns module via Promise
System.module(source, options);
// Returns module via Promise
System.set(name, module);
// Inline register a new module
System.define(name, source, options?);
// Eval code and register module
To load module in the html
<head>
<module import="my-module.js"></module>
</head>
<head>
<module>
import $ from 'lib/jquery';
console.log('$' in this); // false becaue it won't attach the import to the window global
// globals trapped in module
// Other JS here
console.log(window); // Still can call window
// let x = 1;
Module Tag is force strict mode
</module>
</head>
Like using Q
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then...
if (/* everything turned out fine */) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
return promise;
A promise
can be in 1 of 4 states
- fulfilled: successfully resolved (1)
- rejected: rejected (2)
- pending: hasn't resolved or rejected yet (undefined)
- settled: fulfilled or rejected (1 or 2)
You can use .catch instead of second handler in .then
get('users.all')
.then(function(users) {
myController.users = users;
})
.catch(function() {
delete myController.users;
});
var usersPromise = get('users.all');
var postsPromise = get('ports.everyone');
// Wait until BOTH are settled
Promise.all([usersPromise, postsPromise])
.then(function(results) {
myController.users = results[0];
myController.posts = results[1];
}, function() {
delete myController.users;
delete myController.posts;
});
- Promise.all(iterable); // Wait until all settle
- Promise.race(iterable); // Wait until 1 settles
- Promise.reject(reason); // Create a promise that is already rejected
- Promise.resolve(value); // Create a promise that is already resolved
Generators are functions which can be exited and later re-entered. Useful for long iteration functions, so they can be paused to prevent blocking other functions for too long.
function* myGen() { }
// or
function *myGen() { }
function *three() {
yield 1;
yield 2;
return 3;
}
var geni = three(); // starts the generator but doesn't run it
geni.next(); // runs the function for one iteration. Returns { value: 1, done: false }
geni.next(); // Returns { value: 2, done: false }
geni.next(); // Returns { value: 3, done: true }. This ends the generator.
geni.next(); // Returns { value: undefined, done: true }
It iterates while done = false.
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (var v of foo()) {
console.log(v);
}
// Logs 1, 2, 3, 4, 5
function *foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z); // 5 + 24 + 13
}
var genit = foo(5);
console.log(genit.next()); // { value: 6, done: false }
console.log(genit.next(12)); // { value: 8, done: false }
console.log(genit.next(13)); // { value: 42, done: true }
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings
Awesome list!
I think that in the spread operator is better say that it needs a iterator as parameter, instead of an array... is very fun play with it, there is a cool way to get an array from an iterator with this operator.
function *generator() {
yield 6;
yield 6;
yield 6;
};
const it = generator();
const [...nums] = it;
console.log(nums) /// [6, 6, 6]