By Mahdi Pedramrazi
April 2017
Editorial changes
regular expressions
try/catch exception handling
Some features dropped.
Some feature proposed for Harmony release.
strict mode
JSON
reflection on object properties
revision of 5.0 that corrects some errors in the document
new syntax
classes & modules
iterators & for-of loop
collections
promises
reflection & proxies
exponentiation operator (**) Array.prototype.includes.
Async Functions
Shared memory and atomics
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
function person() {
var name = "john";
var age = 30;
console.log(str); // prints: undefined
if (age > 25) {
var str = "It's time to work harder";
console.log(str); // prints: "It's time to work harder"
}
console.log(str); // prints: "It's time to work harder"
}
function person() {
var name = "john";
var age = 30;
var str;
console.log(str); // prints: undefined
if (age > 25) {
str = "It's time to work harder";
console.log(str); // prints: "It's time to work harder"
}
console.log(str); // prints: "It's time to work harder"
}
hoisted to the top
Text
Hoisted variables are initialized with undefined
var name = "John";
{
let name = "Paul";
console.log(name); // prints: Paul
}
console.log(name); // prints: John
not initialized until they appear in the block
{
console.log(a); // undefined
console.log(b); // Uncaught ReferenceError: b is not defined.
// `b` is declared but it's in TDZ.
if (typeof b === undefined) { // ReferenceError
//..
}
var a;
let b;
}
Accessing too early let references are called TDZ
Initially, storage space (binding) is created for the let variable but the variable remains uninitialized
for (let j=0; j < 5; j++){
setTimeout(function() {
console.log(j); // 0 1 2 3 4
}, 1000)
}
j is redeclared in every iteration.
Creates a new lexical scope in each iteration
for (var i=0; i < 5; i++){
setTimeout(function() {
console.log(i); // 5 5 5 5 5
}, 1000)
}
i is defined once, hoisted to the top of the function.
when setTimeout gets executed i === 5
Creates a read only variable after the initial value is set.
- All const variables should be declared and initialized.
- "const" variables can't get reassigned.
Note: not restricted on the value but on variable assignment.
{
const name = "john";
console.log(name); // john
name = "alex"; // TypeError!
const arr = [1, 2, 3];
arr.push(4);
arr; // [1,2,3,4];
arr = 5; // TypeError.
const a; // Uncaught SyntaxError: Missing initializer in const declaration
}
functions declared inside a block are block scoped.
var a = 1;
if (a === 1) {
function foo() {
console.log("1");
}
} else {
function foo() {
console.log("2");
}
}
foo() // <ES6 environment prints 2.
// ES6 throws ReferenceError
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
use ... behind an any iterable.
spread out to individual values
function numbers( a, b, c ) {
console.log(a, b, c); // 1 2 3
}
numbers(1, 2, 3);
numbers(...[1 ,2 ,3]);
expand value or spreading out in other context
var a = [2, 3, 4];
var b = [1, ...a, 5];
console.log(b); // [1, 2, 3, 4, 5];
use ... gathers set of of values to combine values
function numbers( a, b, ...z ) {
console.log(a, b, z); //1 2 [3, 4, 5]
}
numbers(1, 2, 3, 4, 5);
...args gathers all parameters.
great replacement for arguments object
function numbers(...args) {
args.shift();
console.log(...args); // 2 3 4
}
numbers(1, 2, 3, 4);
function numbers() {
var args = Array.prototype.slice.call(arguments);
args.shift();
console.log.apply(null, args); // 2 3 4
}
numbers(1, 2, 3, 4);
pre ES6
Array-Like object
rest operator
spread operator
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
function foo(x, y) {
x = x || 10;
y = y || 20;
console.log( x + y);
}
foo(0, 6); // 16
Expected 6
0 is falsy
function foo(x = 10, y = 20) {
console.log( x + y);
}
foo(null, 6); // 6 <-- null coerces to 0
foo(0, 6); // 6
foo(5, undefined) // 25 <-- undefined interprets as missing
foo(5, null); // 5 <-- null coerces to 0
ES6 version
undefined interprets as missing
null coerces to 0
function required() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = required()) {
return mustBeProvided;
}
foo(); // Uncaught Error: Missing parameter
Example:
use default values for required parameters
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
function names() {
return ["John", "Alex", "Mike"];
}
var tmp = names(),
john = tmp[0],
alex = tmp[1],
mike = tmp[2];
manually assigning indexed values from an array
function values() {
return {
a: 1,
b: 2;
}
}
var tmp = values(),
a = tmp.a,
b = tmp.b;
manually assigning property values from an object
- manual or structured assignment needs extra variables
- lots of boilerplate code
- no need for additional temp variables.
- much cleaner
function names() {
return ["John", "Alex", "Mike"];
}
var [ john, alex, mike ] = names();
console.log(john, alex, mike); // John Alex Mike
array destructuring
function values() {
return {
a: 1,
b: 2;
}
}
var { a, b } = values();
console.log(a, b); // 1 2
object destructuring
function values() {
return {
a: 1,
b: 2
}
}
var a, b;
({ a, b} = values());
[ c, d ] = [3, 4];
Destructuring is a general assignment operation not a declaration
wrap object destructuring in (...).
- Data gets stored in variable alias or target.
- Source and target variables are the same, no need to change
function values() {
return {
a: 1,
b: 2;
}
}
var { a: firstNumber, b } = values();
console.log(firstNumber, b); // 1 2
console.log(a); // Reference Error
variable alias
{ a, b } // source and target are the same
{ a: a, b: b } // same as above - source and target are the same
variable alias
source
Any valid assignment expression is allowed
for both Array destructuring and Object destructuring.
// Object mapping and transformation
var obj = { a: 1, b: 2 };
var o = {};
({ a: o.x, b: o.y } = obj);
console.log(o.x, o.y) // 1 2
// Array transformation - reordering
var a1 = [ 1, 2, 3],
a2;
[ a2[2], a2[0], a2[1] ] = a1
console.log(a2); // [2, 3, 1]
//-----Mapping Object to Array--------
var obj = { a: 1, b: 2 };
var arr = [];
({ a: arr[0], b: arr[1] } = obj);
console.log(arr[0], arr[1]) // 1 2
//-----Mapping Array to Object--------
var a1 = [ 1, 2, 3],
o = {};
[ o.a, o.b, o.c] ] = a1;
console.log(o.a, o.b, o.c); // 1 2 3
Object/Array mapping and transformations
Map an Array to an Object or visa versa
var arr = [ 1, 2, 3];
var [ ,,c ] = arr
console.log(c); // 3;
var obj = { a: 2 };
var { b } = obj;
console.log(b); // undefined
use "," to skip an index
variable that is not defined will be assigned undefined
var arr1 = [2, 3, 4];
var [ b, ...c ] = arr1;
console.log(c) // [3, 4]
use rest operator ... for gather behaviour
var [ a = 10, b = 4, c] = [undefined, 1, 2];
console.log(a, b, c); // 10 1 2
var { x, y, z = 10, k: KK = 20 } = { x: 1, y: 2, z: 3, k: undefined };
console.log(x, y, z, KK); // 1 2 3 20
Array:
Object:
var [a, [b, c, d], e] = [ 1, [2, 3, 4], 5];
console.log( a, b, c, d, e); // 1 2 3 4 5
var App = {
model: {
Item: function() {}
}
};
var { model: { Item } } = App;
// instead of
var Item = App.model.Item;
destructure nested arrays
destructure object namespaces
function numbers([x, y]) {
console.log(x, y);
}
numbers( [1, 2] ); //1 2
numbers( [1] ); // 1 undefined
numbers( [] ); // undefined undefined
numbers(); // uncaught Exception - TypeError
Default values replace undefined values
array destructuring for parameters
object destructuring for parameters
function numbers({x, y}) {
console.log(x, y);
}
numbers( { x :1, y: 2 ); //1 2
numbers( { x: 1 } ); // 1 undefined
numbers( {} ); // undefined undefined
numbers(); // uncaught Exception - TypeError
uncaught exception
function numbers([x, y = 5] = []) {
console.log(x, y);
}
numbers(); // undefined 5
array destructuring for parameters
default object properties overwrites destructured values.
function numbers({x = 1, y = 1} = { x: 3}) {
console.log(x, y);
}
numbers(); // 3 1
numbers({}); // 1 1
Assign default array or object when no parameter is passed.
Assign default values for destructured variables
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
Properties that are the same name as lexical identifier can get shorten
var a = 1;
var b = 2;
var o = {
a: a,
b: b
};
a: a looks redundant
var a = 1;
var b = 2;
var o = {
a,
b
};
new way: function shorthand
var o = {
a: function() { ... },
b: function() { ... }
};
old way
var o = {
a() { ... },
b() { ... }
};
var str = "string";
var o = {
["some string"]: 1,
["some " + str]() { ... }
};
any valid expression can appear inside the [...]
var animal = {
eat() {
console.log("animal:eat");
}
};
var dog = {
eat() {
super.eat();
console.log("dog:eat");
}
};
Object.setPrototypeOf( dog, animal );
dog.eat(); // animal:eat
// dog:eat
Object.getPrototypeof(dog) // animal object
super is allowed only in consise methods.
super is locked statically to the prototype of dog
Concise method imply anonymous function expression.
not a good idea for recursion or event binding
function func(obj) { ... }
func({
someMethod() {
if(...) {
return someMethod(); //uncaught exception - ReferenceError
}
}
});
something: function() {} // anonymous function expression
something: function something() {} // named function expression
Inner function used for recursion
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
function example(a, b) {
a *= 2;
b *= 3;
return a + b;
}
var example = (a, b) => {
a *= 2;
b *= 3;
return a + b;
}
a regular function declaration
Arrow function
Arrow functions are always anonymous function expressions.
var f1 = (a, b) => {
a++;
b++;
return a + b;
};
var f3 = (x, y) => x + y;
No parameter always needs ( )
Single Parameter: no need for ( )
var f1 = x => x * 2;
2+ Parameter: requires ( )
More than one statement requires { ... }
var f1 = () => 5;
var a = [1, 2, 3];
var b = a.map(function(n) {
return n * 2;
});
console.log(b) // [2, 4, 6];
old style
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b) // [2, 4, 6];
new style
var controller = {
makeRequest: function(){
var self = this;
btn.addEventListener("click", function() {
self.makeRequest(...);
}
}
};
var controller = {
makeRequest: function(){
btn.addEventListener("click", () => {
this.makeRequest(...);
}
}
};
Lexical Scope
Dynamic Scope
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
var arr = [1, 2, 3, 4];
for (let val of arr) {
console.log(val); // 1 2 3 4
}
var arr = [1, 2, 3, 4];
var val;
for (var i = 0; i < arr.length; i++) {
val = arr[i];
console.log(val); // 1 2 3 4
}
pre ES6
ES6
var arr = [1, 2, 3, 4];
for (let val of arr) {
console.log(val); // 1 2 3 4
}
var a = [1, 2, 3, 4];
for (
let ret,
it = a[Symbol.iterator]();
( ret = it.next()) && !ret.done; )
{
console.log(ret.value); // 1 2 3 4
}
under the hood
ES6
- Block Scope Declaration
- Spread/Rest Operator
- Default Parameter Values
- Destructuring
- Object Literal Extensions
- Arrow Functions
- for..of loop
- Symbols & Iterators
Note:
var symbol = Symbol("some description");
typeof symbol // "symbol"
new Symbol() // Uncaught TypeError: Symbol is not a constructor
Note:
var EVT_LOGIN = Symbol("event.login");
evthub.listen( EVT_lOGIN, () => { ... });
Available in the function scope
var EVT_LOGIN = Symbol.for("event.login");
evthub.listen( EVT_lOGIN, () => { ... });
Global symbol registry: Symbol.for("...");
var s = Symbol.for("event.login");
var desc = Symbol.keyFor(s);
console.log( desc ); // event.login
use Symbol.keyFor() to extract the key
var arr = [1, 2, 3];
var it = arr[Symbol.iterator]();
it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }
it.next(); // { value: undefined, done: true }
array example
Symbol.iterator is a built in symbol
var m = new Map();
m.set( "key 1", 1 );
m.set( "key 2", 2);
var it = m.entries();
it.next(); // { value: ["key 1", 1], done: false }
var it = m[Symbol.iterator]();
it.next(); // { value: ["key 1", 1], done: false }
Map example
.entries() returns an iterator
var Fib = {
[Symbol.iterator]() {
var n1 = 1;
var n2 = 1;
return {
//make the iterator an iterable
[Symbol.iterator]() { return this; },
next() {
var current = n1;
[n1, n2] = [n2, n1 + n2];
if(current > 10 ) {
return { done: true}
}
return { value: current, done: false};
},
// will get called on break, continue, throw, return
return(v) {
return { value: v, done: true }
}
}
}
};
for (var v of Fib) {
console.log(v);
}
var iterator = Fib[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
You Don't Know JS: ES6 & Beyond
by Kyle Simpson
Understanding ECMAScript 6: The Definitive Guide for JavaScript Developers
By Nicholas c Zakas