Nội dung bài viết
Video học lập trình mỗi ngày
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