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()
    
  • array 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 block) 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}!`
    }
    

    Note that self 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)

    let o = {
      name: 'name',
      myMethod: function() {
        setTimeout( () => {
          console.log(this.name) // o.name
        }, 3000)
      }
    }
    
  • destructuring with default 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 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}. 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. Use .entries() to get index
    for(let [i, car] of cars.entries()) { car }`
    

    To convert HtmlCollection to array you can use Array.from(htmlCollection)

  • 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
    m = new Map([['color', 'red'],['owner', 'me']])
    m.size
    m.keys()
    m.values()
    m.delete('color')
    m.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 array because you can not acsess 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]].flat(Infinity) and
    ['My dog', 'is awesome'].flatMap(words => words.split(' '))
    //[ 'My', 'dog', 'is', 'awesome' ]
    
  • create object from entries newPerson = Object.fromEntries(Object.entries(person))

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

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 http://krasimirtsonev.com/blog/article/javascript-library-starter-using-webpack-es6

You can export any top level function, class, var, let or const.

// lim/math.js
export function sum(x, y) {
  return x + y;
}
export let pi = 3.141593;

// or you could use export list
export { sum, pi };

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)

let myObject = {
  field1: value1,
  field2: value2
};
export {myObject as default};
// or better use shorthand
export default {
  field1: value1,
  field2: value2
};

Note that with import you should use brackets if there is no default export, and check the form of the export command. 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 { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
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()