ES6 Features

Nội dung bài viết

Arrow Functions

A short hand notation for function(), but it does not bind this in the same way.

var odds = evens.map(v => v + 1);  // no parentes and no brackets
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

How does this work?

var object = {
    name: "Name", 
    arrowGetName: () => this.name,
    regularGetName: function() { return this.name },
    arrowGetThis: () => this,
    regularGetThis: function() { return this }
}

console.log(this.name)
console.log(object.arrowGetName());
console.log(object.arrowGetThis());
console.log(this)
console.log(object.regularGetName());
console.log(object.regularGetThis());

//Ouput

this.name ->
object.arrowGetName() ->
object.arrowGetThis() -> [object Window]
this -> [object Window]
object.regularGetName() -> Name
object.regularGetThis() -> {"name":"Name"}

They work well with classes

class someClass {
    constructor() {
        this.name = "Name"
    }

    testRegular() {
        return function() { return this }

    }

    testArrow() {
        return () => this.name;
    }
}

var obj = new someClass();

console.log(obj.name)
console.log(obj.testRegular()());
console.log(obj.testArrow()());

//Ouput

obj.name -> Name
obj.testRegular()() -> undefined
obj.testArrow()() -> Name

Classes

As we know them from "real" languages. Syntactic sugar on top of prototype-inheritence.

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

Enhanced Object Literals

var theProtoObj = {
  toString: function() {
    return "The ProtoOBject To string"
  }
}

var handler = () => "handler"


var obj = {
    // __proto__
    __proto__: theProtoObj,

    // Shorthand for ‘handler: handler’
    handler,

    // Methods
    toString() {

     // Super calls
     return "d " + super.toString();
    },

    // Computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};

console.log(obj.handler)
console.log(obj.handler())
console.log(obj.toString())
console.log(obj.prop_42)

//Ouput

obj.handler -> () => "handler"
obj.handler() -> handler
obj.toString() -> d The ProtoOBject To string
obj.prop_42 -> 42

Tips: ES2020 Features 

String interpolation

Nice syntax for string interpolation

var name = "Bob", time = "today";

var multiLine = `This

Line

Spans Multiple

Lines`


console.log(`Hello ${name},how are you ${time}?`)
console.log(multiLine)

//Ouput

`Hello ${name},how are you ${time}?` -> Hello Bob,how are you today?
multiLine -> This Line Spans Multiple Lines

Destructuring

// list "matching"
var [a, , b] = [1,2,3];
console.log(a)
console.log(b)

//Ouput

a -> 1
b -> 3

Objects can be destructured as well.

nodes = () => { return {op: "a", lhs: "b", rhs: "c"}}
var { op: a, lhs: b , rhs: c } = nodes()
console.log(a)
console.log(b)
console.log(c)

//Ouput

a -> a
b -> b
c -> c

Using Shorthand notation.

nodes = () => { return {lhs: "a", op: "b", rhs: "c"}}

// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = nodes()

console.log(op)
console.log(lhs)
console.log(rhs)

//Ouput

op -> b
lhs -> a
rhs -> c

Can be used in parameter position

function g({name: x}) {
  return x
}

function m({name}) {
  return name
}

console.log(g({name: 5}))
console.log(m({name: 5}))

//Ouput

g({name: 5}) -> 5
m({name: 5}) -> 5

Fail-soft destructuring

var [a] = []
var [b = 1] = []
var c = [];
console.log(a)
console.log(b);
console.log(c);

//Ouput

a -> undefined
b -> 1
c -> []

Default

function f(x, y=12) {
  return x + y;
}

console.log(f(3))

//Ouput

f(3) -> 15

Spread

In functions:

function f(x, y, z) {
  return x + y + z;
}
// Pass each elem of array as argument
console.log(f(...[1,2,3]))

//Ouput

f(...[1,2,3]) -> 6

In arrays:

var parts = ["shoulders", "knees"];
var lyrics = ["head", ...parts, "and", "toes"]; 

console.log(lyrics)

//Ouput

lyrics -> ["head","shoulders","knees","and","toes"]

Spread + Object Literals

We can do cool stuff with this in object creations.

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

// Spread properties
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
console.log(obj)

Sadly it is not supported yet:

npm install --save-dev babel-plugin-transform-object-rest-spread

Rest

We can allow unlimited params to function by using the rest operator.

function demo(part1, ...part2) {
    return {part1, part2}
}

console.log(demo(1,2,3,4,5,6))

//Ouput

demo(1,2,3,4,5,6) -> {"part1":1,"part2":[2,3,4,5,6]}

Let

Let is the new var. As it has "sane" bindings.

{
   var globalVar = "from demo1"
}

{
   let globalLet = "from demo2";
}

console.log(globalVar)
console.log(globalLet)

//Ouput

globalVar -> from demo1
globalLet -> ReferenceError: globalLet is not defined

However, it does not assign anything to window:

let me = "go";  // globally scoped
var i = "able"; // globally scoped

console.log(window.me); 
console.log(window.i);

//Ouput

window.me -> undefined
window.i -> able

It is not possible to redeclare a variable using let:

let me = "foo";
let me = "bar"; 
console.log(me);

//Ouput

SyntaxError: Identifier 'me' has already been declared
var me = "foo";
var me = "bar"; 
console.log(me)

//Ouput

me -> bar

Const

Const is for read-only variables.

const a = "b"
a = "a"

//Ouput

TypeError: Assignment to constant variable.

It should be noted that const objects can still be mutated.

const a = { a: "a" }
a.a = "b"
console.log(a)

//Ouput

a -> {"a":"b"}

For..of

New type of iterator, an alternative to for..in. It returns the values instead of the keys.

let list = [4, 5, 6];

console.log(list)

for (let i in list) {
   console.log(i);
}

//Ouput

list -> [4,5,6]
i -> 0
i -> 1
i -> 2

let list = [4, 5, 6];

console.log(list)


for (let i of list) {
   console.log(i); 
}

//Ouput

list -> [4,5,6]
i -> 4
i -> 5
i -> 6

Iterators

The iterator is a more dynamic type than an array.

let infinite = {
  [Symbol.iterator]() {
    let c = 0;
    return {
      next() {
        c++;
        return { done: false, value: c }
      }
    }
  }
}

console.log("start");

for (var n of infinite) {
  // truncate the sequence at 1000
  if (n > 10)
    break;
  console.log(n);
}

//Ouput

"start" -> start
n -> 1
n -> 2
n -> 3
n -> 4
n -> 5
n -> 6
n -> 7
n -> 8
n -> 9
n -> 10

Using Typescript interfaces we can see how it looks:

interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}
interface Iterable {
  [Symbol.iterator](): Iterator
}

Generators

Generators create iterators, and are more dynamic than iterators. They do not have to keep track of state in the same manner and do not support the concept of done.

var infinity = {
  [Symbol.iterator]: function*() {
    var c = 1;
    for (;;) {   
      yield c++;
    }
  }
}

console.log("start")
for (var n of infinity) {
  // truncate the sequence at 1000
  if (n > 10)
    break;
  console.log(n);
}

//Ouput

"start" -> start
n -> 1
n -> 2
n -> 3
n -> 4
n -> 5
n -> 6
n -> 7
n -> 8
n -> 9
n -> 10

Using typescript again to show the interfaces.

interface Generator extends Iterator {
    next(value?: any): IteratorResult;
    throw(exception: any);
}

function* Iterators and generator An example of yield*

function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function* generator(i) {
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value);

//Ouput

gen.next().value -> 10
gen.next().value -> 11
gen.next().value -> 12
gen.next().value -> 13
gen.next().value -> 20

Unicode

ES6 provides better support for Unicode.

var regex = new RegExp('\u{61}', 'u');

console.log(regex.unicode)
console.log("\uD842\uDFD7")
console.log("\uD842\uDFD7".codePointAt())

//Ouput

regex.unicode -> true
"𠯗" -> 𠯗
"𠯗".codePointAt() -> 134103

Modules & Module Loaders

Native support for modules.

import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const

export expression;
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;

Set

Sets as in the mathematical counterpart where all items are unique. For people who know SQL this is equivalent to distinct.

var set = new Set();
set.add("Potato").add("Tomato").add("Tomato");
console.log(set.size)
console.log(set.has("Tomato"))

for(var item of set) {
   console.log(item)
}

//Ouput

set.size -> 2
set.has("Tomato") -> true
item -> Potato
item -> Tomato

WeakSet

The WeakSet object lets you store weakly held objects in a collection. Objects without a reference will be garbage collected.

var item = { a:"Potato"}
var set = new WeakSet();
set.add({ a:"Potato"}).add(item).add({ a:"Tomato"}).add({ a:"Tomato"});
console.log(set.size)
console.log(set.has({a:"Tomato"}))
console.log(set.has(item))

for(let item of set) {
   console.log(item)
}

//Ouput

set.size -> undefined
set.has({a:"Tomato"}) -> false
set.has(item) -> true
TypeError: set is not iterable

Map

Maps, also known as dictionaries.

var map = new Map();
map.set("Potato", 12);
map.set("Tomato", 34);

console.log(map.get("Potato"))


for(let item of map) {
   console.log(item)
}


for(let item in map) {
   console.log(item)
}

//Ouput

map.get("Potato") -> 12
item -> ["Potato",12]
item -> ["Tomato",34]

Other types than strings can be used.

var map = new Map();
var key = {a: "a"}
map.set(key, 12);


console.log(map.get(key))
console.log(map.get({a: "a"}))

//Ouput

map.get(key) -> 12
map.get({a: "a"}) -> undefined

WeakMap

Uses objects for keys, and only keeps weak reference to the keys.

var wm = new WeakMap();

var o1  = {}
var o2  = {}
var o3  = {}


wm.set(o1, 1);
wm.set(o2, 2);
wm.set(o3, {a: "a"});
wm.set({}, 4);

console.log(wm.get(o2));
console.log(wm.has({}))

delete o2;

console.log(wm.get(o3));

for(let item in wm) {
   console.log(item)
}


for(let item of wm) {
   console.log(item)
}

//Ouput

wm.get(o2) -> 2
wm.has({}) -> false
wm.get(o3) -> {"a":"a"}
TypeError: wm is not iterable

Proxies

Proxies can be used to alter objects' behaviour. They allow us to define traps.

var obj = function ProfanityGenerator() {
    return {
       words: "Horrible words"    
    }
}()

var handler = function CensoringHandler() {
        return {
        get: function (target, key) {
            return target[key].replace("Horrible", "Nice");
        },
    }

}()

var proxy = new Proxy(obj, handler);

console.log(proxy.words);

proxy.words -> Nice words

The following traps are available:

var handler =
{
  get:...,
  set:...,
  has:...,
  deleteProperty:...,
  apply:...,
  construct:...,
  getOwnPropertyDescriptor:...,
  defineProperty:...,
  getPrototypeOf:...,
  setPrototypeOf:...,
  enumerate:...,
  ownKeys:...,
  preventExtensions:...,
  isExtensible:...
}

Symbols

Symbols are a new type. Can be used to create anonymous properties.

var typeSymbol = Symbol("type");

class Pet {

  constructor(type) {

    this[typeSymbol] = type;

  }
  getType() {
     return this[typeSymbol];
  }

}


var a = new Pet("dog");
console.log(a.getType());
console.log(Object.getOwnPropertyNames(a))


console.log(Symbol("a") === Symbol("a"))

//Ouput

a.getType() -> dog
Object.getOwnPropertyNames(a) -> []
Symbol("a") === Symbol("a") -> false

Inheritable Built-ins

We can now inherit from native classes.

class CustomArray extends Array {

}

var a = new CustomArray();

a[0] = 2
console.log(a[0])

//Ouput

a[0] -> 2

It is not possible to override the getter function without using Proxies of arrays.

New Library

Various new methods and constants.

console.log(Number.EPSILON)
console.log(Number.isInteger(Infinity))
console.log(Number.isNaN("NaN"))

console.log(Math.acosh(3))
console.log(Math.hypot(3, 4))
console.log(Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2))

console.log("abcde".includes("cd") )
console.log("abc".repeat(3) )


console.log(Array.of(1, 2, 3) )
console.log([0, 0, 0].fill(7, 1) )
console.log([1, 2, 3].find(x => x == 3) )
console.log([1, 2, 3].findIndex(x => x == 2)) 
console.log([1, 2, 3, 4, 5].copyWithin(3, 0)) 
console.log(["a", "b", "c"].entries() )
console.log(["a", "b", "c"].keys() )
console.log(["a", "b", "c"].values() )

console.log(Object.assign({}, { origin: new Point(0,0) }))

//Ouput

Number.EPSILON -> 2.220446049250313e-16
Number.isInteger(Infinity) -> false
Number.isNaN("NaN") -> false
Math.acosh(3) -> 1.7627471740390859
Math.hypot(3, 4) -> 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) -> 2
"abcde".includes("cd") -> true
"abc".repeat(3) -> abcabcabc
Array.of(1, 2, 3) -> [1,2,3]
[0, 0, 0].fill(7, 1) -> [0,7,7]
[1, 2, 3].find(x => x == 3) -> 3
[1, 2, 3].findIndex(x => x == 2) -> 1
[1, 2, 3, 4, 5].copyWithin(3, 0) -> [1,2,3,1,2]
["a", "b", "c"].entries() -> {}
["a", "b", "c"].keys() -> {}
["a", "b", "c"].values() -> {}
Object.assign({}, { origin: new Point(0,0) }) -> ReferenceError: Point is not defined

Binary and Octal

Literals for binary and octal numbering.

console.log(0b11111)
console.log(0o2342)

console.log(0xff); // also in es5

//Ouput

0b11111 -> 31
0o2342 -> 1250
0xff -> 255

Promises

The bread and butter for async programing.

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("1"), 101)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("2"), 100)
})

Promise.race([p1, p2]).then((res) => {
   console.log(res)
})

Promise.all([p1, p2]).then((res) => {
   console.log(res)
})

//Ouput

res -> 2
res -> ["1","2"]

Quick Promise

Need a quick always resolved promise?

var p1 = Promise.resolve("1")
var p2 = Promise.reject("2")

Promise.race([p1, p2]).then((res) => {
   console.log(res)
})

//Ouput

res -> 1

Fail fast

If a promise fails, all and race will reject as well.

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("1"), 1001)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => reject("2"), 1)
})

Promise.race([p1, p2]).then((res) => {
   console.log("success" + res)
}, res => {
   console.log("error " + res)
})

Promise.all([p1, p2]).then((res) => {
   console.log("success" + res)
}, res => {
   console.log("error " + res)
})

//Ouput

"error " + res -> error 2
"error " + res -> error 2

Reflect

New type of meta programming with new API for existing and also a few new methods.

var z = {w: "Super Hello"}
var y = {x: "hello", __proto__: z};

console.log(Reflect.getOwnPropertyDescriptor(y, "x"));
console.log(Reflect.has(y, "w"));
console.log(Reflect.ownKeys(y, "w"));

console.log(Reflect.has(y, "x"));
console.log(Reflect.deleteProperty(y,"x"))
console.log(Reflect.has(y, "x"));

//Ouput

Reflect.getOwnPropertyDescriptor(y, "x") -> {"value":"hello","writable":true,"enumerable":true,"configurable":true}
Reflect.has(y, "w") -> true
Reflect.ownKeys(y, "w") -> ["x"]
Reflect.has(y, "x") -> true
Reflect.deleteProperty(y,"x") -> true
Reflect.has(y, "x") -> false

Tail Call Optimization

ES6 should fix ensure tail calls do not generate stack overflow. (Not all implementations work).

function factorial(n, acc = 1) {
    if (n <= 1) return acc; 
    return factorial(n - 1, n * acc);
}
console.log(factorial(10))
console.log(factorial(100))
console.log(factorial(1000))
console.log(factorial(10000))
console.log(factorial(100000))
console.log(factorial(1000000))

//Ouput

factorial(10) -> 3628800
factorial(100) -> 9.332621544394418e+157
factorial(1000) -> Infinity
factorial(10000) -> RangeError: Maximum call stack size exceeded
factorial(100000) -> RangeError: Maximum call stack size exceeded
factorial(1000000) -> RangeError: Maximum call stack size exceeded

Repush from: codetower.github.io

Có thể bạn đã bị missing