Focus onThe front small OuRead more original technical articles

Related codes →

10.14 the closure

  • closureRefers to theReferences another function scope variableFunction, usually implemented in nested functions (If a function accesses its external variables, it is a closure)
    • A closure has references to external function variables in its scope chain
    • In order for closure functions to be accessible in the global scope, the inner closure function is usually returned within the outer function
    • Therefore, live objects referenced by closures of external functions cannot be destroyed after the execution of the external function and remain in memory
    • To free memory, unreference the closure function to the external function’s active object
function arraySort(key, sort) {
  return function (a, b) {
    // An internal anonymous function that references the external function variables key and sort to form a closure
    let va = a[key]
    let vb = b[key]
    if (sort === 'asc' || sort === undefined || sort === ' ') {
      // order: va > vb
      if (va > vb) return 1
      else if (va < vb) return -1
      else return 0
    } else if (sort === 'desc') {
      // reverse order: va < vb
      if (va < vb) return 1
      else if (va > vb) return -1
      else return 0}}}let compareNames = arraySort('name') // Executes the function and returns an anonymous function whose scope chain still has references to arraySort's active objects key and sort, so it will not be destroyed
let result = compareNames({ name: 'Nicholas' }, { name: 'Matt' }) // Execute an anonymous function
compareNames = null // Unreference the anonymous function from the arraySort live object to free memory
Copy the code

10.14.1 this object

  • inInside the closureusethisIt makes your code more complicated. If the inner function doesn’t use the arrow function,thisBinding to theThe context in which the function is executed:
    • Called in a global function, not in strict modethisPoint to thewindow, the strict mode is equal toundefined
    • As a method call on an object,thisPoint to that object
    • Anonymous functionsDoes not bind to an object, whichthisPoint to theThe object that calls the anonymous function
global.identity = 'The Window' // vscode is the node running environment. It cannot recognize the global object Window
let object = {
  identity: 'My Object'.getIdentityFunc() {
    return function () {
      return this.identity
    }
  },
}
console.log(object.getIdentityFunc()) ƒ () {return this.identity}, return an anonymous function
console.log(object.getIdentityFunc()()) // 'The Window', immediately call The anonymous function to return this.identity, this refers to The global object
Copy the code
  • A variable is automatically created when a function is calledthisandarguments.The inner function cannot directly access these two variables of the outer functionFor accessSave its reference to another variable that the closure can access
let object2 = {
  identity: 'My Object'.getIdentityFunc() {
    let that = this // The external function variable this is stored in that
    return function () {
      return that.identity // The closure refers to that, which refers to this in the context of getIdentityFunc() (not this in the closure).}}},console.log(object2.getIdentityFunc()()) // 'My Object', immediately calling the anonymous function returns thate. identity that refers to this of the closure external function getIdentityFunc
Copy the code
  • Some special casesthisvalue
let object3 = {
  identity: 'My Object'.getIdentity() {
    return this.identity
  },
}
console.log(object3.getIdentity()) // 'My Object'
console.log(object3.getIdentity) // [Function: getIdentity], getIdentity()
console.log((object3.getIdentity = object3.getIdentity)) // [Function: getIdentity], the Function getIdentity() is assigned to object3.getidEntity
console.log((object3.getIdentity = object3.getIdentity)()) // 'The Window', an anonymous function is called globally immediately after assignment. This refers to The global object
console.log((object3.funcA = object3.getIdentity)()) // 'The Window', The function getIdentity() assigns The same result to other attributes of The object
object3.funcB = object3.getIdentity
console.log(object3.funcB()) // 'My Object', called after assignment, this refers to object3
Copy the code

10.14.2 Memory Leakage

  • Due to the different garbage collection mechanisms used, closures in IE prior to IE9 cause problems: once HTML elements are stored in the scope of a closure, they are not destroyed
function assignHandler() {
  let element = document.getElementById('someElement')
  element.onclick = () = > {
    console.log(element.id) // Element is an active object that references an external function. The anonymous function always exists so that element is not destroyed}}function assignHandler2() {
  let element = document.getElementById('someElement')
  let id = element.id // Save the variable ID of element.id
  element.onclick = () = > {
    console.log(id) // Instead of referring to element directly, reference the variable ID that holds element.id
  }
  element = null // Unreference the Element object to free the closure memory
}
Copy the code

10.15 Expression of the function to be called immediately

  • Anonymous functions called immediately are also called immediately called function expressions (IIFE), which are similar to function declarations and are enclosed in parentheses
; (function () {
  // block-level scope}) ()Copy the code
  • ES5 does not yet support block-level scopes, which can be simulated using IIFE
; (function () {
  for (var i = 0; i < 3; i++) {
    console.log(i) // 0, 1, 2
  }
})()
console.log(i) // ReferenceError: I is not defined, I is in the function body scope (simulated block level scope)
Copy the code
  • ES6 supports block-level scope and does not require IIFE to achieve the same functionality
{
  let i = 0
  for (i = 0; i < 3; i++) {
    console.log(i) // 0, 1, 2}}console.log(i) // ReferenceError: i is not defined

for (let i = 0; i < 3; i++) {
  console.log(i) // 0, 1, 2
}
console.log(i) // ReferenceError: i is not defined
Copy the code
  • When executing the click handler, the value of the iteration variable is the final value at the end of the loop, which can be locked with IIFE or block-level scope for each click to display
let divs = document.querySelectorAll('div')
for (var i = 0; i < divs.length; ++i) {
  divs[i].addEventListener(
    'click'.// Wrong way to write: print directly (click handler to iterate variable value is the final value at the end of the loop)
    // function(){
    // console.log(i);
    // }

    // A function expression that executes immediately, locking the value to be displayed each time
    (function (_i) {
      return function () {
        console.log(_i)
      }
    })(i) // The argument is passed in as the value to be displayed each time)}for (let i = 0; i < divs.length; ++i) {
  // Create a separate variable for each loop inside the loop using the let keyword
  divs[i].addEventListener('click'.function () {
    console.log(i)
  })
}
Copy the code
  • Similarly, when timeout logic is executed, the value of the iteration variable is the value that causes the loop to exit, and the IIFE or block-level scope can be used to lock the value of each iteration
for (var i = 0; i < 5; i++) {
  // The timeout logic is executed after exiting the loop, and the iteration variable holds the value 5 that caused the loop to exit
  setTimeout(() = > {
    console.log(i) // 5, 5, 5, 5, 5, 5
  }, 0)}for (var i = 0; i < 5; i++) {
  // Pass in the current index for each loop with the immediate-call function expression, locking the index value that should be displayed for each timeout logic; (function (_i) {
    setTimeout(() = > {
      console.log(_i) // 0, 1, 2, 3, 4
    }, 0)
  })(i)
}

for (let i = 0; i < 5; i++) {
  // Use let declarations: declare new iteration variables for each iteration loop
  setTimeout(() = > {
    console.log(i) // 0, 1, 2, 3, 4
  }, 0)}Copy the code

10.16 Private variables

  • Any variable defined in a function or block can be considered private (the variable cannot be accessed outside the function or block).
  • Private variables include function parameters, local variables, and other functions defined inside the function
function add(num1, num2) {
  // 3 private variables: num1, num2, local variable sum
  let sum = num1 + num2
  return sum
}
Copy the code
  • A privileged method is a public method that can access a function’s private variables (and private functions) and can be implemented in a constructor
function MyObject() {
  let privateVariable = 10
  function privateFunction() {
    console.log('privateFunction')
    return false
  }
  // privileged methods (closures) : access the privateVariable privateVariable and the private method privateFunction()
  this.publicMethod = function () {
    console.log('privateVariable', privateVariable++)
    return privateFunction()
  }
}
let obj = new MyObject()
obj.publicMethod()
/* privateVariable 10 privateFunction */
Copy the code
  • As with the disadvantages of constructors, the problem with implementing private variables in constructors is that each instance recreates the method (private & privileged), and the Function object with the same mechanism is instantiated multiple times
function Person(name) {
  /* The private variable name cannot be accessed directly, only the privileged getName() and setName() methods can read and write */
  this.getName = function () {
    return name
  }
  this.setName = function (_name) {
    name = _name
  }
}
let person = new Person('Nicholas') // Create a method every time you create an instance (private & privileged)
console.log(person.getName()) // 'Nicholas'
person.setName('Greg')
console.log(person.getName()) // 'Greg'
Copy the code

10.16.1 Static Private Variables

  • useAnonymous function expressioncreatePrivate scopeTo implement the privileged method:
    • definePrivate variablesandPrivate methods
      • Private variables are shared as static private variables, but do not exist in every instance
    • defineThe constructor
      • Use function expressions to define constructors (function declarations create internal functions)
      • Define constructors without keywords, so that they are created in global scope
    • definePublic methods (privileged methods)
      • Defined on the constructor prototype
; (function () {
  /* Anonymous function expression to create a private scope */

  // Private variables and private methods are hidden
  let privateVariable = 10
  function privateFunction() {
    return false
  }

  Constructor: uses function expressions & does not use keywords (created in global scope)
  MyObject = function () {}

  // Public/privileged methods (closures) : defined on the prototype of the constructor
  MyObject.prototype.publicMethod = function () {
    console.log('privateVariable', privateVariable++)
    return privateFunction()
  }
})()
Copy the code
  • In this way, private variables and private methods are shared by the instance, and privileged methods are defined on the stereotype and shared by the instance
  • Creating an instance does not recreate the method, but calling privileged methods and modifying static private variables affects all instances
; (function () {
  // Private variable name is hidden
  let name = ' '

  // constructor, created in global scope
  Person = function (_name) {
    name = _name
  }

  // The privileged method, defined on the constructor prototype
  Person.prototype.getName = function () {
    return name
  }
  Person.prototype.setName = function (_name) {
    name = _name
  }
})()

let person1 = new Person('Nicholas')
console.log(person1.getName()) // 'Nicholas'
person1.setName('Matt')
console.log(person1.getName()) // 'Matt'

let person2 = new Person('Michael')
console.log(person2.getName()) // 'Michael', calls privileged methods and modifies static private variables
console.log(person1.getName()) // 'Michael', affects all instances
Copy the code

10.16.2 Module mode

  • insingletonBased on the extension throughThe scope chainAssociate private variables with privileged methods:
    • Extends the object literal of a singleton to a function expression that is invoked immediately
    • Inside anonymous functions, define private variables and private methods
    • Inside anonymous functions, object literals containing only publicly accessible properties and methods are returned
let singleton = (function () {
  /* An immediate function expression that creates a private scope */

  // Private variables and private methods are hidden
  let privateVariable = 10
  function privateFunction() {
    return false
  }

  // Returns object literals containing only publicly accessible properties and methods
  return {
    publicProperty: true.publicMethod() {
      console.log(++privateVariable)
      return privateFunction
    },
  }
})()
console.log(singleton) // { publicProperty: true, publicMethod: [Function: publicMethod] }
singleton.publicMethod() / / 11
Copy the code
  • In essence, the pattern defines a common interface to a singleton in terms of object literals
function BaseComponent() {} / / BaseComponent components

let application = (function () {
  let components = new Array(a)// Create a private array components
  components.push(new BaseComponent()) // Initialize to add a new instance of the BaseComponent component to the array

  /* Public interface */
  return {
    // getComponentCount() privilege method: Returns the number of registered components
    getComponentCount() {
      return components.length
    },
    // registerComponent() privilege method: registers the component
    registerComponent(component) {
      if (typeof component === 'object') {
        components.push(component)
      }
    },
    // getRegistedComponents() privilege method: view registered components
    getRegistedComponents() {
      return components
    },
  }
})()

console.log(application.getComponentCount()) / / 1
console.log(application.getRegistedComponents()) // [BaseComponent {}], the BaseComponent component is registered

function APPComponent() {} / / APPComponent components
application.registerComponent(new APPComponent()) // Register the APPComponent
console.log(application.getComponentCount()) / / 2
console.log(application.getRegistedComponents()) // [BaseComponent {}, APPComponent {}], registered BaseComponent and APPComponent
Copy the code

10.16.3 Module Enhancement mode

  • usingThe module patternIn theBefore returning an objectFor enhancement, fitA singleton is an instance of a particular type, but you must add additional properties or methods to itScenario:
    • Inside anonymous functions, define private variables and private methods
    • Creates an instance of a (specific) type inside an anonymous function
    • Add common properties and methods to instance objects (enhanced)
    • Return instance object
function CustomType() {} // Specific type
let singleton2 = (function () {
  // Private variables and private methods are hidden
  let privateVariable = 10
  function privateFunction() {
    return false
  }

  // Create instances of a specific type
  let object = new CustomType()

  // Add public attributes and methods
  object.publicProperty = true
  object.publicMethod = function () {
    console.log(++privateVariable)
    return privateFunction
  }

  // Return the instance
  return object
})()
console.log(singleton2) // CustomType { publicProperty: true, publicMethod: [Function: publicMethod] }
singleton2.publicMethod() / / 11
Copy the code
  • In order toThe module patterntheSingleton public interfaceIf, for example,applicationIt must beBaseComponentAn instance of a component can be created using the module enhancement mode:
let application2 = (function () {
  let components = new Array(a)// Create a private array components
  components.push(new BaseComponent()) // Initialize to add a new instance of the BaseComponent component to the array
  let app = new BaseComponent() // Create local variables to hold instances

  /* Public interface */
  // getComponentCount() privilege method: Returns the number of registered components
  app.getComponentCount = function () {
    return components.length
  }
  // registerComponent() privilege method: registers the component
  app.registerComponent = function (component) {
    if (typeof component === 'object') {
      components.push(component)
    }
  }
  // getRegistedComponents() privilege method: view registered components
  app.getRegistedComponents = function () {
    return components
  }

  return app // Return the instance}) ()console.log(application2) // BaseComponent { getComponentCount: [Function (anonymous)], registerComponent: [Function (anonymous)], getRegistedComponents: [Function (anonymous)] }
console.log(application2.getComponentCount()) / / 1
console.log(application2.getRegistedComponents()) // [BaseComponent {}], the BaseComponent component is registered

application2.registerComponent(new APPComponent()) // Register the APPComponent
console.log(application2.getComponentCount()) / / 2
console.log(application2.getRegistedComponents()) // [BaseComponent {}, APPComponent {}], registered BaseComponent and APPComponent
Copy the code
Private variables & private methods Privilege method disadvantages
The constructor In the example, independent In the instance Recreate methods per instance (private & privileged)
Private scope Private scope, static, shared On the constructor prototype Call privileged methods to modify private variables, affecting other instances
The module pattern Private scope, independent On a singleton object
Module enhancement mode Private scope, independent On instance objects

Summary & questions

  • What is a closure? What is its role?
  • Where does this point in global and local method calls without the arrow function? What about this in an anonymous function?
  • How do inner functions access this and arguments of outer functions when functions are nested?
  • What is an immediately called function expression? Use code to simulate block-level scopes
  • Get all div elements and click on different divs to display their corresponding index values. Require IIFE and block-level scopes respectively
  • Please use code to achieve the function: 1 second after the realization of 0~4 digital iteration. Require IIFE and block-level scopes respectively
  • What are private variables? What might it include?
  • What are privileged methods? Write a piece of code that implements privileged methods in constructors and say what’s wrong with this approach
  • Write a piece of code that implements privileged methods through private variables. Describe and demonstrate the limitations of this approach
  • Write code to implement Web component registration by defining the common interface of a singleton object through the module pattern
  • What scenarios does the module enhancement pattern fit into? Use code to register Web components in its mode