Es6 Esnext
Esnext
https://github.com/lukehoban/es6features https://medium.freecodecamp.org/es5-to-esnext-heres-every-feature-added-to-javascript-since-2015-d0c255e13c6e https://blog.bitsrc.io/6-tricks-with-resting-and-spreading-javascript-objects-68d585bdc83
ES6 is ES2015, ES7 is published on 2016 and so on (latest is ES#{curent_year%2010+(month > june) ? 1 : 0 }). ES6 features:
-
defining variable was using
var a
(in strict mode, which is default in ES6, error is raised for using undefined variables). If it was defined inside function, it was visible only inside it, otherwise global scope. It should be declarad at the beggining of a function, because it can be still referecenced before declaration and used like a function parameter (hoisting). ES6 introducedlet
andconst
with block scope. For example insideif () { let myVar=1; }
(myVar
is not accesible outside ofif
block). Also you can NOT use variable before welet variable
. Also you can NOT redeclare same variable name (or if name is same as param declarationfunction f(myName) { let myName; }
. Notice difference infor (var i = 1; i< 5; i++) { console.log(i) setTimeout(function() { console.log(i) # here we have i = 5,5,5,5 since same variable is used # if we used let i=1;i<5;i++ than it will be different variable: 1,2,3,4 }, 1000) }
const MY_CONST = document.querySelect(".my-class");
have block scope, and it should be used for all non volatile variables. If you use object than nested values can changeconst k = { a: 3 }; k.a = 4
- interpolation of template literals with backticks and curly braces
var name = "Duke"; console.log(`Hello ${name}`); const multiLine = `this is multi line` // use trim() so you can go to new line at beggining let n = ` hi bye`.trim()
-
arrow functions are abbreviated syntax for anonymouse functions. If there is only one parameter than you can omit parenthesis, if there are no params than you can replace parenthesis with
_
. One liner syntax (without curly braces) use implicit return, for other you need explicit return if you need. If returning object, remember to wrap inside parethensesvar sayHello = (name) => ({name: `Hello ${name}!`}) var sayCiao = name => { return `Ciao ${name}!` }
- arrow functions automatically bind
this
class MyClass { regular() { return function() { console.log('regular this: ', this) // undefined } } arrow() { return () => { console.log('arrow this: ', this) // MyClass } } }
Note that
this
inside arrow functions does not bind to current object for object methods. It binds to outerthis
(it is inherited from the execution context). Sothis
is usefull only if you have function inside method that is defined in clasical way, ie do not use arrow functions for methods, use old functions (when dynamic context is not needed). Also for jQuery each$.each(functions() {$(this).select2()})
use normal function instead of arrow function.let o = { name: 'name', myMethod: function() { setTimeout( () => { console.log(this.name) // o.name }, 3000) } }
-
destructuring with default parameters values and renaming https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
# destructuring arrays let [, month, date = 1] = '2010-10-11'.split('-') # month = '10' and date = '11' let [f, s, ...rest] = [1, 2, 3, 4] # f=1, s=2, rest = [3,4] # destructuring objects let { name, age, gender:sex = 'male' } = { name: 'Duke', age: '33', gender: 'male' } # is the same as let name = o.name let age = o.age let sex = o.gender # here we also renaming and have default value
You can also use destructuring when defining methods
function call({ name: 'My Name', phone: '123123', } = {}) { console.log(name + ' ' + phone) }
You can also destructure nested objects in function params
var car = { model: 'bmw 2018', engine: { turbo: true, vin: 12345 } } // es6 shorthand instead of // { vin: vin } // you can use just // { vin } const modelAndVIN = ({model, engine: {vin}}) => { console.log(`model: ${model} vin: ${vin}`); } modelAndVIN(car); // => model: bmw 2018 vin: 12345
There exists rest
...
parameter which convers to arrayconst sum = (...a) => a.reduce((sum, current) => sum + current, 0)
and same operator
...
inside method call is called spread and it expands explode array to params (coffee calls this splatsf(items...)
)let a = [1, 2, 3] f(...a) # is the same as f(1, 2, 3)
you can also spread objects
let d = { a: 3}; let db = { ...d, b: 3}
. But if object has methods, it wont be expanded. If you spread two objects, later will override:let object1 = { a:1, b:2 }; let object2 = { b:30, c:40}; let merged = {…object1, …object2} // {a:1, b:30, c:40}
. Merging nested properties you need to anotate each property when spreading untill you have one level objectlet a = { p: { x: 1 } } let b = { p: { y: 2 } } let m = { p: { ...a.p, ...b.p } }
for default values you can use something like this so
passed.nested_parameter
does not override whole objecto.nested
but only properties ofo.nested
o = { parameter: 1, ...passed, nested: { nested_parameter: 2, ...passed.nested_parameter }, }
Note that is you have only
{ parameter: 1, ...passed }
than it will override wholeo.nested
withpassed.nested
. For array you can clone (duplicate) withconst cloned = [...array]
You can de-duplicate array withlet arr = [1, 1, 2, 2, 3, 3]; let deduped = [...new Set(arr)] // [1, 2, 3]
. - default parameter values
function g(a=2){}
. It can be combined with default destructing paramsfunction g({a=1, b}={b:2}){}
andg({a: 2})
(ok b=2),g()
(ok a=1, b=2), butg({})
error, b is required. -
if you need some param to be required you can use this trick
const required = () => {throw new Error('Missing parameter')}; //The below function will trow an error if either "a" or "b" is missing. const add = (a = required(), b = required()) => a + b; add(1, 2) //3 add(1) // Error: Missing parameter.
for(i=0;i<cars.length;i++) {}
is not concisecars.forEach(myFunction)
is concise but can not break out of the loop. ES6 givesfor(let ... of ..) {}
(for values in array or string) andfor..in
(for object property names) is concise and can break.for in
iterates over enumerable properties of an object in arbitrary order,for of
iterates over values of iterable object. Use.entries()
to get indexfor(let [i, car] of cars.entries()) { car }` for(let id in data) { let value = data[id] }
To convert HtmlCollection to array you can use
let array = Array.from(htmlCollection)
andarray.forEach(item) => { console.log(item) }
To create new array you can useArray(9).fill(null)
.array.reduce(function, init_value)
can be used as filtering ifinit_value
is array, than function takes two arguments (init_value, array_item)const numbers = [10, 20, 30, 40]; const doubledOver50 = numbers.reduce((finalList, num) => { num = num * 2; //double each number (i.e. map) //filter number > 50 if (num > 50) { finalList.push(num); } return finalList; }, []); doubledOver50; // [60, 80]
-
class Person
can be used to instantiate objectsnew Person()
. For initialization you can useconstructor(name) { this.name = name }
Instead of hash methodfull_name: function() {}
we can writefull_name () {}
This is shorthand for object method definitions https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions I like to put space since it does not look like we are calling a function.Extend classes
class Employee extends Person {}
so we don’t need to writeEmployee.prototype = new Person
(orvar e = { __proto__: person }
. Insideclass
we can define instance andstatic
methods. Static methods are called without instantiating their class (used for utility functions). Multiple inheritance can be achieved with mix-ins https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Mix-ins You can use getter (or setter) which binds an object property to a function that will be called when that property is looked up (or attempt to set that propertye.full_name =
)class Empoyee extends Person { constructor(name) { super(name); this.proffessional_name = name; } get full_name() { return this.name; } set full_name(name) { this.name = name; } static myUtilityFunction(uppercase) { return uppercase; } hello() { return `Hi, I'm ${name}` } }
-
new data structure called
Map
andWeakMap
(keys are objects, not plain values like number, string or symbol)var map = new Map(); map.set('year', '123'); map.get('year'); // '123' # or use initialization map = new Map([['color', 'red'],['owner', 'me']]) # or map = new Map(Object.entries(object)) # convert back to object object = Object.fromEntries(map) # some methods map.size map.keys() map.values() map.delete('color') map.clear()
- new data structure called
Set
andWeakSet
- new string functions
'asd'.startsWith('a');'asd'.endsWith('a');'asd'.includes('as');'a'.repeat(3);
- new Array functions
Array.from([1,2,3], x => x + x)
Array.find( user => user.age > 15)
same asArray.findIndex
but returns object instead of index[1,2,3].filter( (n) => n > 2 )
. This can be used as.compact
in ruby for example sum of digitssum = value .toString() .split('') .filter(Number) .map(Number) .reduce(function (a, b) { return a + b; }, 0);
-
[1,2].includes(3)
returns false -
property value shorthands, when you define object and you already have variable with same name as key, you can use
const name = 'dule' const anPerson = { name } // is the same as const aPerson = { name: name }
it also exists for methods
const aPerson = { speak (word) {}, // same as speak: function (word) {}, // do not use arrow because you can not access this // speak: () => {}, }
-
computed object property names (you can use variable as key)
const name = 'dule' const aPerson = { 'mile': 'value', [name]: 'dule is key and value', // same as 'dule': 'dule is key and value' [name+'car']: 'value', // { 'dulecar': 'value' } }
- generators
function *calculator(input) { var doubleThat = 2 * (yield (input / 2)) var another = yield (doubleThat) return (input * doubleThat * another) }
const calc = calculator(10) // start > c.next() { value: 5, done: false } > c.next(7) { value: 14, done: false } > c.next(100) { value: 14000, done: true }
ES2017
- in function declarations and function calls you can use trailing commas
doSomething(p1, p2,)
ES2019 ESNext
[1,[2, [3]]].flat(Infinity)
returns[1, 2, 3]
- instead of
map
andflat
you can useflatMap
['My dog', 'is awesome'].flatMap(words => words.split(' ')) // [ 'My', 'dog', 'is', 'awesome' ]
- create object from entries
Object.fromEntries(Object.entries(person))
and usage is in transforming the objectsconst object = { x: 42, y: 50, abc: 9001 } const result = Object.fromEntries( Object.entries(object) .filter(([key, value]) => key.length === 1) .map(([key, value]) => [key, value * 2]) ) // { x: 84, y: 100 }
- class property can be marked as private with hash
class Counter { #count = 0 get value() { return this.#count } increment() { this.#count++ } } const counter = new Counter() counter.#count // SyntaxError
- underscore in numbers:
1_000
.
Promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
Promise is an object representing eventual completion or failure of an
asynchronous operation.
We can use chaining to combine two callback function, so
promise.then(callback1).then(callback2)
means than callback1
will be
executed and than callback2
.
promise1 = promise.then(successCallback, failureCallback)
returns promise
which is completion of promise
and successCallback
or failureCallback
which also can be asynchronous functions returning a promise (in which case any
callbacks added to promise1 will be queued behind those promises returned by
successCallback
or failureCallback
).
If we attach handler to already settled promise, handler will be performed at
the first asynchronous opportunity (stack has cleared and clock tick has passed,
like setTimeout(handler, 10)
).
.catch(failureCallback)
is short-hand for then(null, failureCallback)
.
Callback param is result of previous promise so using arrow functions
doSomething()
.then(result => doSomethingElse(result))
.then(elseResult => doThirdThing(newResult))
In .catch().then()
if catch does not throw an error, .then
will be performed
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
a Promise
is a proxy for a value not necessarily known when the promise is
created. It can be in one of three states: pending
(initial), fulfilled
or
rejected
. When it is either fulfilled
or rejected
(also known as
settled
) than all associated handlers queued up using then
are called.
A promise is fulfilled if promise.then(f)
will call f
as soon as possible
A promise is rejected if promise.then(null, r)
will call r
as soon as
possible.
A promise is pending if it is neither fulfilled nor rejected.
https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md Promises have two exclusive fates: resolved (when trying to resolve or reject it has no effect on promise) and unresolved.
Signature of handler functions are simple, they accept a single parameter of any
type and termination condition determines the settled state of next promise in
the chain. Termination with throw nextReason
creates a rejected state, other
termination, for example return nextValue
creates resolved state. If nextValue
is another promise object, it is dynamically inserted into the chain. In this
case it is resolved
but still is not settled
.
Creating a new promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
new Promise(executor)
constructor is used to wrap functions that do not
already support promises (like setTimeout, XMLHttpRequest). Argument executor
is function that accepts two functions function(resolutionFunc, rejectionFunc)
{}
which we use in callbacks (in setTimeout or XMLHttpRequest.onload) which are
tethered to the promiseObj. Return value is not important, side effect is that
promiseObj is resolved and output is communicated by calling resolutionFunc or
rejectionFunc.
const promiseObj = new Promise((resolutionFunc, rejectionFunc) => {
try {
setTimeout(() => {
resolutionFunc('foo')
}, 300)
}
catch(err) {
rejectionFunc('bar')
}
})
Here is example of function which returns a promise
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onload = () => resolve(xhr.responseText)
xhr.onerror = () => reject(xhr.statusText)
xhr.send()
});
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
When return value of handler is some value, the promise returned by .then()
gets resolved with returned values as its value
When handler throes an error, promise returned by .then()
gets rejected with
the thrown error as its value
When it returns another promise (fulfilled, rejected or pending) than promise
returned by .then()
gets fulfilled, rejected or is in pending state.
Promise.all([p1, p2]).then
will be executed when all are resolved
Promise.race([p1, p2]).then
will be executed when first (any) is resolved
Both of this methods will be rejected if some of p1 is rejected - reject early.
await Promise.allSettled([p1, p2])
all are finished (some failed or
succeeded).
Promise.any([p1, p2])
is similar to race
but does not reject early when one
of the promises rejects. We end up in catch
block when all were rejected.
Async/Await
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout( () => resolve('Here is thing I built after some delay'), 2000);
})
}
const doSomething = async () => {
console.log(await doSomethingAsync())
}
Using async
to a function means that it will return promise, so those two
functions are the same:
const aFunction = async () => {
return 'test'
}
const aFunction = () => {
return Promise.resolve('test')
}
aFunction().then(console.log)
ES2018 there is for await (const line of readLines(filePath)) { log(line) }
.finnaly()
is executed regardless of the successful or not promise.
Modules
Import bindings which are exported by another module (js file). Module is just
js script (no need for special keyword) but everything defined inside module is
local to the module, except when you use export
.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
function myModule() {
this.hello = function() {
return 'hello
}
}
module.exports = myModule
Note that for Webpack you can use CommonJS module and use with require
var myModule = require('myModule');
var myModuleInstance = new myModule();
myModuleInstance.hello(); // 'hello!'
Using Asynchronous Module Definition AMD can load modules async using define
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
console.log(myModule.hello());
});
Universal Module Definition UMD combines AMD and CommonJS. But ECMASCRIPT 6
introduce import
and export
methods which replace all those functions…
So if you write libs for web you should create all variations so it can be
used in webpack or directly
https://medium.com/@kelin2025/so-you-wanna-use-es6-modules-714f48b3a953
You can export any top level function, class, var, let or const. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
// lim/math.js
export function sum(x, y) {
return x + y;
}
// you could use export list
export { sum, pi };
// but the best is to use export multiple times
export CONST PI = 3.141593
export CONST MY_KEY = '3.141593'
You can name the export as default
and in this case you can use anonymous
functions (otherwise you have to name each export so it can be distinquished
from other exports). In case export is default than you can import only one
item.
let myObject = {
field1: value1,
field2: value2
};
export {myObject as default};
// or better use shorthand
export default myObject
# do not export default and list
# export default { myobject }
For index files you can export each file
// interfaces/index.ts
export * from './person.interface'
export * from './sport.interface'
and import all in one line
import { Person, Sport } from './interfaces/index'
Note that with import
you should use curly brackets if there is no default
export, and check that you used correct variable name (same as in the export
command).
Note that when using webpack, when export default sum
than you need to call
const myModule = require('my_module'); myModule.default.sum()
https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
Default import
// import what is default exported and bind bind to defaultExport
import defaultExport from "module-name";
// this is shorthand of
import { default as defaultExport } from 'module-name';
Namespace import
import * as name from "module-name";
Named import
import { export1 , export2 as alias2 , [...] } from 'module-name'
combination of default import and named or namespaces import
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
// just run the code, not importing anything
import "module-name";
module-name is relative (./lib/math
or lib/math
) or absolute path name to
the .js
file
import * as math from "lib/math";
console.log("2pi = " + math.sum(math.pi, math.pi));
// or we could
import { pi, sum } from "lib/math";
console.log("2pi = " + sum(pi, pi));
In html you can include using script tag with type='module'
<html>
<head>
<meta charset="utf-8"/>
<script type='module' src='./app.js'></script>
</head>
<body>
asd up is
<span id='up'></span>
</body>
</html>
# app.js
import toUp from './t.js'
document.getElementById('up').innerHTML = toUp('asd')
# t.js
export default (str) => str.toUpperCase()
NPM package
-D
is shorthand for --safe-dev
.
npm i -D @babel/core babel-loader
DOM
- instead of
element.dataset('activateTarget')
camelCase, we can useelement.getAttribute('data-activate-target')
snake case. - findElementById
getElementById()
- https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassNamefinding
getElementsByClassName('class)')
returns liveHTMLCollections
(similar to array, and it is updated when document is changed) - https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
const elements = document.querySelectorAll(".class");
returns staticNodeList
(which provides.entries()
and.forEach()
). To convert to array you can useArray.from(elements)
orArray(...elements)
.elements.forEach(function(element) { element.remove() })
Tips
arguments
object contains function arguments, but better is to use...arguments
as rest parameter- one liner if statement
if (text.lenth == 0) return
- jquery
$(selector).addClass('b')
can be written aselement.classList.add('b')
orelements.forEach((element) => element.classList.add('b') )
. Also fortoggle
andremove
. other examples http://youmightnotneedjquery.com/ - string substring
str.substring(1, str.length(-2))
can be replaced with slicestr.slice(1, -2)
- compare arrays with json, for example check if array is sorted
let c = [...x] if (JSON.stringify(c.sort((a,b) => a - b)) == JSON.stringify(x)) {
-
insert html
el.insertAdjacentHTML('beforeend', entries)
-
fetch
makes js requests. If you need to pass header for cookie session than use param https://github.com/github/fetch#sending-cookiesfetch('/users', { credentials: 'same-origin' })
when you do POST whan you also need csrf token
let data = { token: '123' } fetch('/profile/add-token', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-Token': csrf, }, body: JSON.stringify(data) })
todo https://css-tricks.com/all-the-new-es2019-tips-and-tricks/