Esnext

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 introduced let and const with block scope. For example inside if () { let myVar=1; } (myVar is not accesible outside of if block). Also you can NOT use variable before we let variable. Also you can NOT redeclare same variable name (or if name is same as param declaration function f(myName) { let myName; }. Notice difference in

    for (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 change const 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 parethenses

    var 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 outer this (it is inherited from the execution context). So this 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 array

    const 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 splats f(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 object

    let 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 object o.nested but only properties of o.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 whole o.nested with passed.nested. You can de-duplicate array with let 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 params function g({a=1, b}={b:2}){} and g({a: 2}) (ok b=2), g() (ok a=1, b=2), but g({}) 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 concise cars.forEach(myFunction) is concise but can not break out of the loop. ES6 gives for(let ... of ..) {} (for values in array) and for..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 index
    for(let [i, car] of cars.entries()) { car }`
    
    for(let id in data) {
      let value = data[id]
    }
    

    To convert HtmlCollection to array you can use Array.from(htmlCollection) To create new array you can use Array(9).fill(null).

  • array.reduce(function, init_value) can be used as filtering if init_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 objects new Person(). For initialization you can use constructor(name) { this.name = name } Instead of hash method full_name: function() {} we can write full_name(){} Extend classes class Employee extends Person {} so we don’t need to write Employee.prototype = new Person (or var e = { __proto__: person }. Inside class we can define instance and static 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 property e.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 and WeakMap (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 and WeakSet
  • 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 as Array.findIndex but returns object instead of index
    • [1,2,3].filter( (n) => n > 2 )
    • [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

    const name = 'dule'
    
    const aPerson = {
      'mile': 'value',
      [name]: 'dule is key and value',
      [name+'car']: '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)
  • instead of map and flat you can use flatMap
    ['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 objects
    const 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

let done = true

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = 'Here is the thing I built'
    resolve(workDone)
  } else {
    const why = 'Still working on something else'
    reject(why)
  }
})

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.

// 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

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 webpack webpack-cli eslint-loader

Tips

  • arguments object contains function arguments, but better is to use ...arguments as rest parameter
  • one liner if statement `if (text.lenth == 0) return

todo https://css-tricks.com/all-the-new-es2019-tips-and-tricks/