Modern Javascript: Everything You've Missed Out In The Last 10 Years

JavaScript has come a long way since I knew it as the letter "D" in DHTML. For anyone like me who didn't want to use the latest syntax that might require polyfills or a transpiler, I wrote this cheat sheet to get you familiar with all the benefits that are widely supported in modern browsers.



Content



  • Array functions
  • const / let
  • Nullish coalescing ?? and Optional chaining? .. operators
  • Async / Await
  • Arrow functions () => {}
  • for ... of
  • for await ... of
  • Classes
  • get / set
  • function default parameters
  • function named parameters
  • function rest… parameter
  • Destructuring
  • Shorthand functions aka Methods
  • Promise.all
  • Template literals
  • Proxy
  • Module import/export


( , , )





Array functions



Check out all of these new native array functions. No need for underscore or lodash anymore.



Array.every ()

Array.filter ()

Array.find ()

Array.findIndex ()

Array.forEach ()

Array.from ()

Array.includes ()

Array.isArray ()

Array.lastIndexOf ()

Array.map ()

Array.reduce ()

Array.reduceRight ()

Array.some ()



Array docs



const / let



These new keywords declare variables in block scope (as opposed to global or functional scope). Usage const



implies that the value that the value should not change, but let



gives this opportunity.



Let documentation



?? and?.



??



checks if a value is null or undefined. No need to use anymore !!



.



?.



checks if a value is true before calling the next property or function. Extremely useful when dealing with additional props.



Optional chaining documentation



let a, b=1
let result = a ?? b
print(result)

result = (a !== null && a !== undefined) ? a : b;
print(result)
      
print({x:1}?.a?.b ?? "not found")
      
      
      







Async / Await



The async / await keywords are there to save you from callback hell. Use await



to make an asynchronous call look like a synchronous call, i.e. execution await fetchUserName()



will not advance to the next line until fetchUserName () has completed. Please note that in order to use await



, you must be executing a function declared as async, i.e. async function fn () {await fetchUserName ()}.







Async / Await docs.



function fetchUserName() {
  return new Promise(resolve => setTimeout(resolve, 500))
}
      
async function withAsync() {
  print("withAsync: fetching...")
  await fetchUserName()
  print("withAsync: done")
}
await withAsync()
      
function withoutAsync() {
  print("withoutAsync: fetching...")
  fetchUserName().then(()=>print("withoutAsync done"))
}
withoutAsync()
    
      
      







Arrow functions () => {}



These are functions tied to the current context. There are three main types that you will see in the wild:

one argument, one line, many lines.



A single-argument form does not require parentheses, and a single-line form does not require an operator return



; return unconditional.



1 const fn = a => a*2
      
      







One argument. One line.



A multi-line form requires a statement return



if the function intends to return something. Multiple arguments require parentheses.



const fn = (a,b) => {
  console.log(a,b)
  return a*b
}
      
      







Multiple arguments, multiple lines.



Arrow function docs



for ... of



Used to iterate over an iterator. Likewise for...in



, except you don't need to check hasOwnProperty



. You cannot use this looping syntax on an object directly because the object does not have an iterator. Use Object.entries ({})



to get iteration instead .



for ... of docs



const x = {a: 1, b: 2}
for (const [key, value] of Object.entries(x)) {
  print(`${key}=${value}`)
}
      
      







for await ... of



Asynchronous iteration was introduced in 2018. As well as Promise.all



, it can be used to synchronize many asynchronous tasks. The example below shows 3 tasks running asynchronously. The loop processes one result at a time, in order; in this case, the fastest tasks to complete are evident only at the end of the iteration.



for await ... of docs



const delay = (n) => {
  return new Promise((resolve) => {
    setTimeout(()=>{
      print("resolve "+n)
      resolve(n)
    }, n)
  })
}

const delays = [
  delay(150),
  delay(50),
  delay(25)
]

for await (const ret of delays) {
  print("for loop await "+ret)
}
      
      







Classes



In 2015 ES6 ported classes to Javascript. Javascript classes are like classes from other languages ​​that you know and love. Inheritance, class methods, getters and setters, properties, etc.



Class documentation



class A {
  constructor(name) {
    this.name = name
  }
  myProp = "myProp"
  static foo() {
    print("Static method says foo")
  }
}
class B extends A {
  constructor(name, age) {
    super(name)
    this.age = age
  }
  toString() {
    return `${this.name} ${this.age}`
  }
}
A.foo()
const b = new B("Catch", 22)
print(b)
print(b.myProp)
      
      







get / set



Get and set are functions called properties, for example person.age = 16; person.age> 18



. This comes in very handy when you need a dynamic or computed property. And they can be used with both classes and regular objects.



get / set documentation



Classes with getters and setters

class A {
  constructor() {
    this._firstName = "Jane"
    this._lastName = "Smith"
  }
  get fullName() {
    return `${this._firstName} ${this._lastName}`
  }
  set firstName(v) {
    this._firstName = v
  }
}
const a = new A()
print(a.fullName)
a.firstName = "John"
print(a.fullName)
      
      







Objects with getters and setters

const x = {
  get now() { return new Date() }
}
print(x.now)
      
      







function default parameters



Hooray! Now you can specify default parameters in your function definition. Works as you would expect.



Default parameter docs



function greet(msg="Hello world") {
  print(msg)
}
greet()
greet("hi")
      
      







function named parameters



Through the magic of object destructuring, functions can now have named parameters.



Named parameter docs



function greet({name = "Jane", age = 42} = {}){
  print(name + " " +age)
}
greet()
greet({name: "John", age: 21})
      
      







function rest ... parameter



The reset parameter allows the function to accept an arbitrary number of arguments as an array. It is recommended to use this instead arguments



.



Rest parameter docs



function greet(msg1, ...msgs) {
  print(msg1)
  msgs.forEach(s => print(s))
}
greet("hi", "hello", "world")
      
      







Object.assign and spread operator



Object.assign(target, source)



combines two or more objects into one. It modifies the target in place, so if you prefer to create a new object, pass an empty object literal as the first argument.



Alternatively, you can use the spread operator ...



to combine multiple objects: {... obj1, ... obj2}



although keep in mind that spread



will not call setters on an object, so to be most portable consider Object.assign



. The Spread operator can also be used with arrays, as shown in the last code example.



Spread syntax docs



const source = {x: 1, y: 4}
const target = Object.assign({}, source)
print(JSON.stringify(target))

const spread = {a: 1, b: 2, ...source}
print(JSON.stringify(spread))

const ary1 = [1]
const ary = [...ary1, [2,3]]
print(ary)
      
      
      







Destructuring



Destructuring allows you to retrieve values ​​from objects and arrays using templates. This is a complex topic with many applications ... too many for me to list, but I've shown some of the more common uses I can think of.



Destructuring docs and MDN docs



function f() {
  return [1, 2];
}
let [a, b] = f()
print("a="+a + " b=" + b)

const obj = {state: {id: 1, is_verified: false}}
const {id, is_verified: verified} = obj.state
print("id = " + id)
print("verified = " + verified)

for (const [key, value] of Object.entries({a: 1, b: 2, c: 3})) {
  print(key + " is " + value);
}
      
      
      







Shorthand functions aka Methods



Functions declared for objects can use the new shorthand style, which lacks the function keyword.



The two functions (fn1, fn2) are equivalent in the example below.



Method guide



const x = {
  type: "x",
  shorthand() {
    print("shorthand "+this.type)
  },
  long: function() {
    print("long "+this.type)
  }
}
x.shorthand()
x.long()

      
      







Promise.all



I've mostly skipped promises because async / await is preferred, but sometimes you need to synchronize multiple asynchronous calls and Promise.all is the easiest way to do it.



Promise.all documentation



const delay = (n) => {
  return new Promise((resolve) => {
    setTimeout(()=> resolve(n), n)
  })
}
async function main() {
  const delays = [100, 200, 300].map(n => delay(n))
  print("waiting…")
  const res = await Promise.all(delays)
  print("done. result is " + res)
}
main()
      
      
      







Template literals



This new syntax, also known as template strings, provides simple string interpolation and multi-line strings.



Template literal docs



let x = `multi
      line
string`
print(x)

x = `1+1=${1+1}`
print(x)
      
      







Proxy



A proxy allows you to intercept get / set calls on another object. This can be useful for tracking property changes, updating the DOM later, or creating innovative APIs like the www proxy below. Proxy docs



image







let _nums = [1,2,3]
let nums = new Proxy(_nums, {
  set(target, key, value) {
    target[key] = value
    print("set called with " + key + "=" + value)
    print("update DOM")
    return true
  }
})
nums.push(4)
print("nums: " + nums)
print("_nums: " + _nums)
      
      







Module import / export



Modules allow you to create a namespace for your code and break down functionality into smaller files. In the example below, we have a module named greet.js that is included in index.html. Please note that module loading is always deferred, so it does not block HTML rendering. There are many ways to import / export functionality from js files, read the export documentation for details .



Import docs



function greet(msg) {
  console.log("greet:", msg)
}
export default greet
      
      







A file named "greet.js" in the "/ js" directory.



<script type="module">
  import greet from "/js/greet.js"
  greet("hi")
</script>
      
      







index.html



Read more



So, I did not talk about everything that has changed over the past decade, but only about what I find most useful. Check out these other topics.



References





Guides




All Articles