Advanced JavaScript

By Fangtai Dong
Picture of the author
Published on
advanced javascript

Importance of JavaScript in Modern Web Development

JavaScript is a crucial component in modern web development. This scripting language has revolutionized the way people interact with web pages and has made it possible for developers to add a new layer to website interactivity. It has become one of the most popular programming languages over the years. With the advent of new technologies and frameworks, developers are now able to achieve sophisticated website design. In this article, I will explore the key concepts and core knowledge of JavaScript which is an important programming language in modern web development.


Advanced JavaScript Fundation 1

  1. Javascript Engine
  2. Javascript Runtime
  3. Interpreter / Compiler / JIT Compiler
  4. Writing Optimized Code
  5. Call Stack + Memory Heap
  6. Stack Overflow + Memory Leaks
  7. Garbage Collection
  8. Node.js
  9. Single Threaded Model

Advanced JavaScript Fundation 2

  1. Execution Context
  2. Lexical Environment
  3. Scope Chain
  4. Hoisting
  5. Function Invocation
  6. Function Scope vs Block Scope
  7. IIFE (Immediately Invoked Function Expression)
  8. this Keyword in Javascript
  • Definition

    This -- is the object that the function is a property of

    This -- Who call it (refers to whatever is on the left of the .(dot))

  • const obj = {
      property: `I'm a property of obj`,
      method: function(){
        // this refers to the object obj
        console.log(this.property)''
      }
    };
    // I'm a property of obj
    // obj is to the left of the dot
    obj.method();
    
  • this 4 ways:

    1. new keyword binding - the new keyword changes the meaning of this to be the object that is being created (in this example is person1)

    • // new binding
      function Person(name, age) {
        this.name = name
        this.age = age
        console.log(this)
      }
      
      const person1 = new Person('person1', 55)
      // this = Person { name: 'person1', age: 55 }
      
    1. implicit binding - "this" refers to the object that is calling it. It is implied, without doing anything its just how the language works.

    • //implicit binding
      const person = {
        name: 'person',
        age: 20,
        hi() {
          console.log('hi ' + this)
        },
      }
      
      person.hi()
      // this = person { name: 'person', age: 20, hi(){...} }
      
    1. explicit binding - using the "bind" keyword to change the meaning of "this".

    • //explicit binding
      const person3 = {
        name: 'person3',
        age: 50,
        hi: function () {
          console.log('hi ' + this.setTimeout)
        }.bind(window)
      }
      
      person3.hi()
      // hi function setTimeout() { [native code] }
      
    1. arrow functions as methods - "this" is lexically scoped, refers to it's current surroundings and no further. However, if "this" is inside of a method's function, it falls out of scope and belongs to the window object. To correct this, you can use a higher order function to return an arrow function that calls "this".

    • // arrow functions inside objects
      const person4 = {
        name: 'person4',
        age: 40,
        hi: function () {
          var inner = () => {
            console.log(this)
          }
          return inner()
        },
      }
      
      person4.hi()
      // this = person4 { name: 'person4', age: 40, hi() {...} }
      // if either function is changed around, it doesn't work
      
  1. Call Apply Bind in JavaScript
  • Call is a method of an object that can substitute a different object than the one it is written on.

    • const wizard = {
        name: 'Merlin',
        health: 100,
        heal(num1, num2) {
          return (this.health += num1 + num2)
        },
      }
      
      const archer = {
        name: 'Robin Hood',
        health: 30,
      }
      console.log(archer) // health: 30
      
      wizard.heal.call(archer, 50, 20)
      
      console.log(archer) // health: 100
      
    • In this example call is used to borrow the heal method from the wizard and is used on the archer (which is actually pointing this to archer), with the optional arguments added.

  • Apply is almost identical to call, except that instead of a comma separated list of arguments, it takes an array of arguments.

    • // instead of this
      // wizard.heal.call(archer, 50, 20)
      // apply looks like this
      wizard.heal.apply(archer, [50, 20])
      // this has the same result
      
  • Bind Unlike call and apply, bind does not run the method it is used on, but rather returns a new function that can then be called later.

    • console.log(archer) // health: 30
      const healArcher = wizard.heal.bind(archer, 50, 20)
      healArcher()
      console.log(archer) // health: 100
      
  1. Currying with bind
  • Currying is breaking down a function with multiple arguments into one or more functions that each accept a single argument.

  • function multiply(a, b) {
      return a * b
    }
    
    let multiplyByTwo = multiply.bind(this, 2)
    multiplyByTwo(4) // 8
    
    let multiplyByTen = multiply.bind(this, 10)
    multiplyByTen(6) // 60
    

Prototypal inheritance of TikTok assessment

  • function Activity(amount) {
      this.amount = amount
    }
    
    Activity.prototype.setAmount = function (value) {
      if (value <= 0) {
        return false
      } else {
        this.amount = value
        return true
      }
    }
    
    Activity.prototype.getAmount = function () {
      return this.amount
    }
    
  • Why this can be accessed in Activity Prototype ?

  • In JavaScript, when you create a new object instance with the new keyword, the instance inherits from the prototype of its constructor. This is called prototype inheritance. Every JavaScript function (except the arrow function, which does not have this binding) has a prototype attribute, which is an object. When an object is created through this constructor, the new object will inherit the properties and squares of the prototype object. Law.

When you call new Activity (amount), the following things happen:

  • A new empty object was created.

  • Pointing the __proto__ attribute of this new object to Activity.prototype means that it inherits all properties and methods on Activity.prototype.

  • The Activity constructor calls the newly created object as the context (i.e. this points to the new object).

  • Inside the constructor, this.amount sets the amount property of the new object.

Therefore, when you create an instance of Activity through new Activity (amount), the instance has its own amount attribute. When you call the setAmount or getAmount method of the instance, because these methods are defined on Activity.prototype, the JavaScript engine will look along the prototype chain and find these methods from the current object instance. Within these methods, this keyword refers to the object instance that calls the method, that is, the object with the amount attribute.

This is why this.amount can be accessed in the method defined by Activity.prototype: every time the method is called by an object instance, this points to that instance, so you can access the amount property defined in the constructor.

  • Why can use Call() to fullfil protoype inheritance and How ?

    function Payment(amount, receiver) {
      Activity.call(this, amount) // Inherit Activity
      this.receiver = receiver
    }
    
    Payment.prototype = Object.create(Activity.prototype)
    Payment.prototype.constructor = Payment
    
    Payment.prototype.setReceiver = function (receiver) {
      this.receiver = receiver
    }
    
    Payment.prototype.getReceiver = function () {
      return this.receiver
    }
    
  • In JavaScript, inheritance usually refers to the properties and methods of one object directly accessing another object. This mechanism is usually implemented through the prototype chain. However, in this example, Activity.call(this, amount); uses a different method to achieve similar inheritance effects. The call method here is a method of the Function object in JavaScript, which allows you to call a function while changing the direction of this inside the function body. When Activity.call(this, amount); is executed, this keyword in the Activity function will be set to the current Payment instance. Activity here is likely to be a constructor that is used to initialize newly created objects. By using call, you actually pass the Payment instance to Activity as this, so that the activity constructor can set properties and methods for the Payment instance, as if the Payment instance is constructed through Activity. The function is created the same. This method is not a real prototype inheritance, because there is no activity on the prototype chain of Payment. However, it allows the Payment instance to reuse the logic defined in the Activity constructor.

  • More Obvious - notice the comments in below code snippet

    function Activity(amount) {
      this.amount = amount
    }
    
    Activity.prototype.perform = function () {
      // perform some activity
    }
    
    function Payment(amount, receiver) {
      Activity.call(this, amount) // Borrow constructor
      this.receiver = receiver
    }
    
    // Inherit prototype
    Payment.prototype = Object.create(Activity.prototype)
    
    // Set constructor back to Payment after overwriting prototype
    Payment.prototype.constructor = Payment
    
    Payment.prototype.process = function () {
      // process the payment
    }
    
  • In this example, Payment.prototype = Object.create(Activity.prototype); ensures that the Payment object can access the method on the Activity prototype (e.g. perform). At the same time, we also fixed the Payment.prototype.constructor pointer to Payment, because prototype replacement will overwrite the pointer of the constructor.


  • Since ECMAScript 2015 (ES6), JavaScript has introduced the class and extends keywords, making the inherited syntax clearer and easier to understand. Using the syntax of ES6, the above code can be written as:
    class Parent {
      constructor(name) {
        this.name = name
      }
    
      sayName() {
        console.log('Parent name:', this.name)
      }
    }
    
    class Child extends Parent {
      constructor(name, age) {
        super(name) // use parent's constructor(name)
        this.age = age
      }
    
      sayAge() {
        console.log('Age:', this.age)
      }
    }
    
    const child = new Child('Alice', 10)
    child.sayName() // Output: Parent name: Alice
    child.sayAge() // Output: Age: 10
    
  • In this ES6 example, the Child class inherits from the Parent class through the extends keyword. Super(name) in the subclass; calls the constructor of the parent class and passes the necessary parameters.

Types in JavaScript

  • 7 types in JavaScript

    • number
    • string
    • boolean
    • undefined
    • null
    • Symbol('mySymbol')
    • {}
  • prmitive VS non-primitive type

  • pass by value VS pass by reference

  • shallow copy

    • // obj is an object defined before
      let clone = Object.assign({}, obj)
      // or
      let clone2 = { ...obj }
      
  • deep clone

    • let superClone = JSON.parse(JSON.stringify(obj))
      
    • // better deep clone method
      const myDeepCopy = structuredClone(myOriginal)
      
  • type coercion

    • means: the language converting a certain type to another type

    • 1 == '1' //true
      
    • type coercion check

    • false == "" // ture

    • false == [] // ture

    • false == {} // false

    • "" == 0 // ture

    • "" == [] // ture

    • "" == {} // false

    • 0 == [] // ture

    • 0 == {} // false

    • 0 == null // false

      • only have: null == undefined // true
  • Dynamic VS Static typing

  • Weakly VS Strongly typing

  • TypeScript


The 2 Pillars in JavaScript:

  • Higher Order Functions

    • a function that can take a function as an argument
    • or
    • a function that returns another function
    • What is achieved by HOF is the ability to tell the function what to do during invocation, so that more flexibility contained in HOF than ever before. (keep out code dry and flexible)
    // Higher Order Function: multiplyBy
    const multiplyBy => (num1) => (num2) => num1*num2;
    
    multiplyBy(4)(6) // 24
    
  • Functions VS Objects

  • Scheme + Java

  • Function Constructor:

    const four = new Function('return 4')
    
    four() //4
    
  • Functions are first class citizens in JavaScript

    • function can be assigned to a variable
    • function can be passed into arguments
    • return a function as an value
  • define function out of a loop and can run function in a loop

  • it's good to have default parameters set on a function

    function a(param = 6) {
      return param
    }
    a()
    
1. Closures
  • function() + lexical scope = closures

    function a() {
      let a = 'grandpa'
      return function b() {
        let b = 'father'
        return function c() {
          let c = 'son'
          return `${a} > ${b} > ${c}`
        }
      }
    }
    
    a()()() // 'grandpa > father > son'
    
  • Lexical Scoping: It is a scope determination mechanism, in which the scope of a function is determined by the position declared by the function in the code, not by the location of the call. This means that the scope chain of the function when it is created will remain inside the function, no matter where the function is called.

    • Fixed scope chain
      • When a function is defined, its scope chain has been determined, including its own scope and the scope that defines its environment.
    • Function access variables
      • A function can access variables in the scope when it is defined, even if it is in a different scope when the function is called.
    • Closures
      • The lexical scope is the basis of the closure. Closure is a special function that can remember and access the scope of its definition, even if it is called in its external scope.
    • Nested functions
      • In JavaScript, other functions can be defined within the function. The internal function will have access to the scope of external functions, which is determined by the rules of the lexical scope.
  • What does this means: lexical environment === [[scope]] in JavaScript

    • In JavaScript, the two concepts of lexical environment and [scope]] are closely related, but they are usually not completely equivalent. Let's explain these two concepts in detail:

    1. Lexical Environment

    • "Lexical Environment" is an internal concept in JavaScript that defines how variables and functions are accessed and how they are associated with their respective scopes. Each execution context has a lexical environment associated with it, which contains the following two components:

      • Environment Record: This is a structure that stores variables and function declarations. In the lexical environment, this record contains all local variables and declared functions.

      • A reference to the outer environment: This is a reference to the external lexical environment to implement the scope chain. If a variable cannot be found in the current lexical environment, the engine will look for an external lexical environment.

    1. [[Scope]] (scope)

    • [[Scope]] is an internal property of a function object that is set when the function is created. This property contains references to the lexical environment when functions are created, which is why closures can access variables in the environment in which they are defined.

    1. Differences and connections

    • When a function is created, its [[Scope]] property is set to the lexic environment at the time of creation. This means that the function "remembers" the scope chain when it was created.

    • The lexic environment is a broader concept that applies not only to functions, but also to block-level scopes (such as if statements and code blocks in the for loop).

    • In a sense, you can think of the [[Scope]] of a function as a snapshot of its lexical environment, but the lexical environment, as a broader concept, applies to the execution context of any code.

    1. Conclusion

    • To sum up, although the two are closely related, they are not technically exactly the same. [[Scope]] is an internal property of a function object, which refers to the lexical environment when functions are created; and the lexical environment is a broader concept that describes how variables and functions are accessed.
    function CallMeFT() {
      setTimeout(function () {
        console.log(callMe)
      }, 4000)
    
    const callMe = 'Hi! I am Fangtai'
    }
    
    CallMeFT() // 'Hi! I am Fangtai'
    
    
  • Closures Benefits

    • Memory Efficient

      • Because using closure, the big creation of 7000 length array only being created once and it supports multiple times usage without create this heavyDuty data structure again.
    const getHeavyDuty = heavyDuty2() // function hoisting, so it works
    getHeavyDuty(550) // 'somthing'
    getHeavyDuty(6000) // 'somthing'
    getHeavyDuty(50) // 'somthing'
    
    function heavyDuty2() {
      const heavyDutyArray = new Array(7000).fill('somthing')
      console.log('big creation')
    
      return function (index) {
        return heavyDutyArray[index]
      }
    }
    
    • Encapsulation

      • In below code snippet, I encapsulate the launch function from outside world so that not people can access the launch function.
      const makeNuclearButton = () => {
        let timeWithoutDestruction = 0
        const timePass = () => timeWithoutDestruction++
        const totalPeaceTime = () => timeWithoutDestruction
        const launch = () => {
          timeWithoutDestruction = -1
          return 'BOOOOM!'
        }
        setInterval(timePass, 1000)
    
        return {
          totalPeaceTime: totalPeaceTime,
        }
      }
    
  • Exercise

    const array = [1, 2, 3, 4]
    for (var i = 0; i < array.length; i++) {
      setTimeout(function () {
        console.log('I am at Index ' + i)
      }, 3000)
    }
    
    // Output:
    // I am at Index 4
    // I am at Index 4
    // I am at Index 4
    // I am at Index 4
    
    • How do I fix above code snippet to I am at Index 1 2 3 4 as Output
      • using let to alter var in for loop condition
      • using closure and var
    const array = [1, 2, 3, 4]
    for (let i = 0; i < array.length; i++) {
      setTimeout(function () {
        console.log('I am at Index ' + array[i])
      }, 3000)
    }
    
    // Or
    
    const array = [1, 2, 3, 4]
    for (var i = 0; i < array.length; i++) {
      (function(closureI){setTimeout(function () {
        console.log('I am at Index ' + array[closureI])
      }, 3000)})(i)
    }
    
    // Output:
    // I am at Index 1
    // I am at Index 2
    // I am at Index 3
    // I am at Index 4
    
    
    
  • Conclusion

    • Closures allow a function to access variables from an closing scope or outer scope environment even after it leaves the scope in which it was declared.
2. Prototypal Inheritance
  • Almost all objects in Javascript pass down properties through a prototype chain. We call this chain, prototypal inheritance. The child of the object "inherits" properties from its parent. All objects in JavaScript are descended from the Object constructor unless deliberately created or altered to not do so. The objects inherit methods and properties from Object.prototype. The prototype property also has an accessor property called __proto__ that creates a link between the current object and points to the object it was created from, the "prototype".
  • __proto__ points to the prototype up to chain
  • __proto__ property acually lives in prototype
  • To be specific, prototype is prototype object
    prototype chain
    prototype chain
  • Only functions have the prototype property

  • The above sentence mains that the thing contains prototype object is always a function
    • {}.prototype // undefined
      [].prototype // undefined
      'string'.prototype // undefined
      
      Object.prototype // yes. it has
      String.prototype // yes. it has
      
      
  • Excercise
  • Date.prototype.lastYear = function(){
      return this.getFullYear() - 1
    }
    
    new Date(2023-11-11).lastYear() // 2022
    
  • // overwrite the map method in Array
    Array.prototye.map = function(){
      let arr = []
      for (let i = 0; i < this.length; i++){
        arr.push((this[i] + 'MapLogo'))
      }
      return arr
    }
    
    console.log([1,2,3].map()) // 1MapLogo, 2MapLogo, 3MapLogo
    

OOP and FP

  • Benefits of programming paradigms:

    • Clear + Understandable
    • Easy to Extend
    • Easy to Maintain
    • Memory Efficient
    • DRY
  • Factory functions

  • Object.create()

    • Object.create() is a method in JavaScript that creates a new object and sets the prototype of this new object to another object. This means that the newly created object will inherit the properties and methods of the prototype object, but it is an independent object, and its modification will not affect the object as the prototype.
    • const elfFunctions = {
        attack() {
          return 'attack with' + this.weapon
        }
      }
      
      function createElf(name, weapon) {
        let newElf = Object.create(elfFunctions)
        newElf.name = name
        newElf.weapon = weapon
        return newElf;
      }
      
      const david = createElf('David', 'Cannon')
      console.log(david.attack()) // attack with cannon
      
  • Constructor Functions

    • need to use new keyword in Javascript
      • the new keyword automatically returns the object
      • the new keyword also creates the elf constructor
      • any functions that is invoked using the new keyword is called a constructor function
      • constructor functions example: Number() Object()
      • using the Capital letter to identify the constructor function
    • function Elf(name, weapon) {
        this.name = name
        this.weapon = weapon
      }
      
      const david = new Elf('David', 'Cannon')
      
    • How to create constructor function Natively in JavaScript
      • const Efl1 = new Function('name', 'weapon',
        `this.name = name;
        this.weapon = weapon;`)
        
        const daivd = new Efl1('David', 'Cannon')
        david // {name: 'David', weapon: 'Cannon'}
        
    • Every function in JavaScript automatically gets a prototype property
    • About new keyword
      • return a object with this being re-pointed to callable object instead of global object (arrow function not included)
      • build a constructor in the function
      • utilize prototype property to create methods that can be used in a callable object (only for constructor function)
      • function Elf(name, weapon) {
          this.name = name
          this.weapon = weapon
        }
        
        Elf.prototype.attack = function() {
          return 'attack with' + this.weapon
        }
        
        const david = new Elf('David', 'Cannon')
        console.log(david.attack())
        
    • about this in context
      • function Elf(name, weapon) {
          this.name = name
          this.weapon = weapon
        }
        
        Elf.prototype.attack = function() {
          return 'attack with' + this.weapon
        }
        
        Elf.prototype.build = function() {
          function building(){
            return this.name + 'builds a house'
          }
          return building() // undefined
        }
        
        const david = new Elf('David', 'Cannon')
        console.log(david.attack())
        
      • Becasue there is no object call building() inside of build function. To solve it:
        • bind() return building.build(this)
        • variable store const self = this and then, using return self.name + 'builds a house'
        • arrow function const building = () => {return this.name + 'bulds a house'}
    • Primitive values internally in JavaScript are transferred to object
      • var a = 5
        a.toString() // '5'
        
  • ES6 Classes

    • class Elf {
        constructor(name, weapon){
          this.name = name
          this.weapon = weapon
        }
        // Within the class, methods (including constructors and other methods)
        // can be directly pasted without comma separation.
        attack(){
          return 'attack with' + this.weapon
        }
      }
      
    • class: a blueprint of creating an instance
    • instantiation: using new keyword to create an instance through blueprint
    • pseudo classical inheritance (internally JS still uses prototype inheritance)
  • Inheritance

    • // Define a base class
      class Animal {
        constructor(name) {
          this.name = name;
        }
      
        speak() {
          console.log(`${this.name} makes a noise.`);
        }
      }
      
      // Define a subclass that extends the base class
      class Dog extends Animal {
        constructor(name, breed) {
          super(name); // Call the parent class constructor with the name parameter
          this.breed = breed;
        }
      
        bark() {
          console.log(`${this.name} barks.`);
        }
      }
      
      // Create an instance of the Dog class, which is a subclass of Animal
      const myDog = new Dog('Rex', 'German Shepherd');
      
      // Call methods inherited from the parent class and defined in the subclass
      myDog.speak(); // Output: Rex makes a noise.
      myDog.bark(); // Output: Rex barks.
      
    • In this example, Animal is a base class with a constructor and a speak method. The Dog class extends Animal and adds a bark method. It also introduces a new property breed. Notice how the Dog class calls super(name) to pass the name to the constructor of the base class Animal. This is required to ensure that the Animal class is properly initialized before the Dog class adds its own properties and behavior.
  • Private Class Fields + Methods

    • class Employee {
          #name = "Test"; // private field
          setName(name) {
              this.#name = name;
          }
      }
      const emp = new Employee();
      emp.#name = 'New'; // error
      emp.setName('New'); // ok
      
  • OOP in React

  • 4 pillars of OOP

    • Encapsulation
    • Abstraction
    • Inheritance
    • polymorphism
  • Functional Programming

    • side effect: function modifies the global variable outside of function itself
    • pure functions:
      • 1 task
      • return statement
      • pure
      • no shared state
      • immutable state
      • composable
      • predictable
    • referential transparency: It is a concept of functional programming, which means that expressions can be replaced by their values without changing the behavior of the program
    • idempotent: In computer science, "idempotent" is a feature of an operation, which means that the effect of performing the operation multiple times is the same as once. In other words, no matter how many times this operation is performed, the results are consistent
    • Imperative vs Declarative
    • Immutability: Not changing the data, not changing the state. Making a copy and return the new data
  • Higher order functions(HOF) and Closures

    • closures' key point: the inner function which is inside of the outer function rememeber the variable used by or declared in the outer scope
    • To some extent, closure can be used to achieve private data
  • Curry

    • Currying is a technology that converts a multiparametric function into a series of functions that use one parameter. In other words, currying is a process of decomposing function parameters by partially applying parameters
    • const multiply = (a,b) => a*b
      const curriedMultiply = (a) => (b) => a*b
      const curriedMultiplyBy5 = curriedMultiply(5)
      
      curriedMultiplyBy5(4) // 20
      
  • Partial Application

    • const multiply = (a, b, c) => a*b*c
      const partialMultiplyBy5 = multiply.bind(null, 5)
      partialMultiplyBy5(4, 10) // 200
      
  • Memoization

    • Caching: is a way to store values so you can use them later on
    • function memoizedAddTo80() {
        let cache = {}
        return function(n) {
          if (n in cache) {
            return cache[n];
          } else {
            console.log('long time')
            cache[n] = n + 80
            return cache[n]
          }
        }
      }
      const memoized = memoizedAddTo80()
      
      console.log('1', memoized(5))
      console.log('2', memoized(5))
      
  • Compose and Pipe

    • Composing or composition is the idea that any sort of data transformation that we do should be obvious (conveyor belt)
    • compose(), JavaScript doesn't have compose natively so that developer need to use library
    • Ramda - A practical functional library for JavaScript programmers
    • Build own compose function in JavaScript
    • Compose: funcitions are executed from right to left, while Pipe: functions are executed from left to right
    • const mutiplyBy3AndAbsolute = compose(multiplyBy3, makePositive)
      const multiplyBy3 = (num) => num*3
      const makePositive = (num) => Math.abs(num)
      
      const compose = (f, g) => (data) => {f(g(data))}
      const pipe = (f, g) => (data) => {g(f(data))}
      
      
      multiplyBy3AndAbsolute(-50) // 150
      
  • Arity

    • simply means the number of arguments a function takes
  • Object.assign(arg1, arg2, arg3)

    • arg1: new object
    • arg2: thing that want to assign to arg1
    • arg3: update
  • Amazon shopping solution (composable functional programming)

    • ...args this is rest parameter syntax to create an array of what input in it
    • // Amazon shopping
      const user = {
        name: 'David',
        active: true,
        cart: [],
        purchases: []
      }
      
      // 1st ...args is an array [{}, {}]
      // 2nd ...args is {}, {}
      const compose = (f, g) => (...args) => f(g(...args))
      
      // main
      purchaseItem(
        emptyCart,
        buyItem,
        applyTaxToItems,
        addItemToCart
      )(user, {name: 'laptop', price: 200})
      
      function purchaseItem(...fns) {
        return fns.reduce(compose)
      }
      
      function addItemToCart(user, item){
        const updateCart = user.cart.concat(item)
        return Object.assign({}, user, { cart: updateCart })
      }
      
      function applyTaxToItems(user) {
        return user
      }
      
      function buyItem(user) {
        return user
      }
      
      function emptyCart(user) {
        return user
      }
      
    • Note that the definition order of the reduce function is from left to right, but the execution order is from right to left. This means that the last function in the array of functions will be executed first!
  • OOP VS FP

    • OOP: few operations on common data, stateful, side effect, imperative
    • Functional Programming: many operations on fixed data, stateless, pure, declarative

Asynchronous JavaScript

  • Asynchronous Functions are the functions can be executed later

  • How does JavaScript works? (explain the difference between Asynchronous and Synchronous)

    • What is a program: allocate memory & parse and execute
    • The V8 engine consists of 2 parts: memory heap & call stack
    • Web APIs: DOM, AJAX, Timeout
    • Callback Queue: execute the function that in Web APIs
    • Event Loop: check if call stack is empty. If call stack is empty, check callback queue
  • Promise

    • A promise is an object that may produce a single value some time in the future (resolved value or rejected value)

    • A promise may be in one of 3 possible states: fulfilled, rejeceted, or pending

    • Callback function

    • const promise2 = new Promise((resolve, reject) => {
        setTimeout(resolve, 100, 'HI');
      });
      
      promise2.then((value) => {
        console.log(value); // will print 'HI'
      });
      
      
    • Here, setTimeout is set to call the resolve function after 100 milliseconds and pass the string 'HI' as a parameter. This means that promise2 will be resolved (or "cashed") after 100 milliseconds with the value 'HI'

    • In JavaScript, resolve and reject are two functions related to the Promise object, which are used to change the state of the Promise object.

    • const myPromise = new Promise((resolve, reject) => {
        // The code for asynchronous operation is executed here.
        const operationWasSuccessful = true; // Suppose this is based on the result of asynchronous operation.
      
        if (operationWasSuccessful) {
          resolve('Operation succeeded'); // Solve Promise and deliver successful results
        } else {
          reject('Operation failed'); // Reject Promise and pass the reason for the error or failure
        }
      });
      
      myPromise
        .then((result) => {
          console.log(result); // If Promise is resolved, the code here will be executed.
        })
        .catch((error) => {
          console.error(error); // If Promise is rejected, the code here will be executed.
        });
      
      
  • About Promise.all()

    • Promise.all(urls.map(url => {
        return fetch(url).then(resp => resp.json())
      })).then(results => {
        console.log(results[0])
        console.log(results[1])
        console.log(results[2])
      }).catch(()=>cosole.log('error'))
      
  • Async Await

    • underneath the hood, an async function is a function that returns a promise. But the benefit of Async / Await is that it makes code easier to read.
    • The effect of barrier
    • async function asyncProcess() {
        const result1 = await doAsyncTask1(); //The first asynchronous task, waiting to be completed
        console.log(result1); // This line will wait for doAsyncTask1() to resolve before execution
      
        const result2 = await doAsyncTask2();
        console.log(result2);
      
        const result3 = await doAsyncTask3();
        console.log(result3);
        // more...
      }
      
      // Hypothetical asynchronous function, return Promise
      function doAsyncTask1() {
        return new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 1000));
      }
      
      function doAsyncTask2() {
        return new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 1000));
      }
      
      function doAsyncTask3() {
        return new Promise(resolve => setTimeout(() => resolve('Task 3 completed'), 1000));
      }
      
      // Call the asynchronous processing function
      asyncProcess();
      
  • ES9 (2018)

    • Object spread operator
    • const animals = {
        tiger: 23,
        lion: 5,
        monkey: 2,
        bird: 40
      }
      
      function spreadOperator(p1, p2, p3){
        console.log(p1)
        console.log(p2)
        console.log(p3)
      }
      
      const {tiger, lion, ...rest} = animals
      
      spreadOperator(tiger, lion, rest) //23, 5 { monkey: 2, bird: 40 }
      
    • promise: .finally() which means no matter .then() or .catch() is called, finally() will be called
    • for await of feature
    • // urls has been defined with links array
      
      const getData2 = async function() {
        // because fetch with url return promise
        const arrayOfPromises = urls.map(url => fetch(url))
      
        for await (let request of arrayOfPromises) {
          // Block it. Make sure that the data in each loop can be obtained.
          const data = await request.json();
          console.log(data)
        }
      }
      
  • Job Queue (Microtask Queue)

    • where promises are located and has a higher priority than callback queue
  • Parallel, Sequence and Race

    • parallel: Promise.all(promises)
    • race: Promise.race(promises)
    • sequence: Async / Await 1 Await 2 Await 3
  • ES2020

    • Promise.allSettled() this will give the status like fulfilled for resovling and rejected for rejecting
    • Promise.all() has to have all promises resolve and Not throw any error
  • Threads, Concurrency and Parallelism


Modules in JavaScript

  • arr.at(-1) at function of an array provide an access for getting the data from not only positive index but negative index
  • Top level Await (used in Module)

Error Handling

Hi there! Want to support my work?

© 2023 Fangtai Dong. All rights reserved.