current position:Home>Learning notes of JavaScript advanced programming | 10.14-10.16. Closure

Learning notes of JavaScript advanced programming | 10.14-10.16. Closure

2021-08-23 03:02:16 Xiao Ao

Focus on Front end small Acura , Read more original technical articles

Related codes →

10.14 Closure

  • Closure Refer to Reference to a variable in the scope of another function Function of , It is usually implemented in nested functions ( If a function accesses its external variables , So it's a closure

    • The scope chain of the function in the closure , There are references to external function variables
    • in order to Closure functions can be accessed at the global scope , Usually inside an external function return Internal closure function
    • So the external function The active object referenced by the closure , and You can't Destroyed after external function execution , Still in memory
    • If you want to Free memory , Need to be Dereference the closure function to the external function activity object
function arraySort(key, sort) {
  return function (a, b) {
    //  Internal anonymous function , Reference external function variables key and sort, To form a closure 
    let va = a[key]
    let vb = b[key]
    if (sort === 'asc' || sort === undefined || sort === '') {
      //  positive sequence :va > vb
      if (va > vb) return 1
      else if (va < vb) return -1
      else return 0
    } else if (sort === 'desc') {
      //  In reverse order :va < vb
      if (va < vb) return 1
      else if (va > vb) return -1
      else return 0
    }
  }
}
let compareNames = arraySort('name') //  Execute the function and return the anonymous function , The scope chain of anonymous functions is still valid for arraySort Active objects of key and sort With reference , So it won't be destroyed 
let result = compareNames({ name: 'Nicholas' }, { name: 'Matt' }) //  Executing anonymous functions 
compareNames = null //  Disallow anonymous function pairs arraySort References to active objects , Free memory 

10.14.1 this object

  • stay In closure Use this Will make the code more complex , If the inner function does not use the arrow function ,this Bind to The context in which the function is executed

    • Call in global function , Non strict mode this Point to window, Strict mode is equal to undefined
    • Call as a method of an object ,this Point to the object
    • Anonymous functions Not bound to an object , Its this Point to The object that calls the anonymous function
global.identity = 'The Window' // vscode yes node Running environment , Unrecognized global object window, The test will be window Change it to global
let object = {
  identity: 'My Object',
  getIdentityFunc() {
    return function () {
      return this.identity
    }
  },
}
console.log(object.getIdentityFunc()) // ƒ () { return this.identity }, Return anonymous function 
console.log(object.getIdentityFunc()()) // 'The Window', Immediately call the anonymous function to return this.identity,this Point to global object 
  • The variable is automatically created when the function is called this and arguments, Internal functions cannot directly access these two variables of external functions , If you want to access Save its reference to another variable that the closure can access
let object2 = {
  identity: 'My Object',
  getIdentityFunc() {
    let that = this //  Variables of external functions this Save in that in 
    return function () {
      return that.identity //  Closure ( Anonymous functions ) I quote that,that Point to getIdentityFunc() Contextual this( Not in a closure this)
    }
  },
}
console.log(object2.getIdentityFunc()()) // 'My Object', Immediately call the anonymous function to return that.identity,that Point to the function outside the closure getIdentityFunc Of this
  • In some special cases this value
let object3 = {
  identity: 'My Object',
  getIdentity() {
    return this.identity
  },
}
console.log(object3.getIdentity()) // 'My Object'
console.log(object3.getIdentity) // [Function: getIdentity], function getIdentity()
console.log((object3.getIdentity = object3.getIdentity)) // [Function: getIdentity], function getIdentity() Assign a value to object3.getIdentity
console.log((object3.getIdentity = object3.getIdentity)()) // 'The Window', The anonymous function is called globally immediately after assignment ,this Point to global object 
console.log((object3.funcA = object3.getIdentity)()) // 'The Window', function getIdentity() Assign to other properties of the object , The result is the same 
object3.funcB = object3.getIdentity
console.log(object3.funcB()) // 'My Object', After assignment object3 call ,this Point to object3

10.14.2 Memory leak

  • Due to the use of different garbage collection mechanisms , Closure in IE9 Previous IE Browsers can cause problems : once HTML The element is stored in the scope of a closure , It will not be destroyed
function assignHandler() {
  let element = document.getElementById('someElement')
  element.onclick = () => {
    console.log(element.id) //  An active object that references an external function element, Anonymous functions always exist, so element Will not be destroyed 
  }
}

function assignHandler2() {
  let element = document.getElementById('someElement')
  let id = element.id //  preservation element.id The variable of id
  element.onclick = () => {
    console.log(id) //  Don't quote directly element, Change to reference to save element.id The variable of id
  }
  element = null //  Release right element References to objects , Free closure memory 
}

10.15 The function expression that is called immediately

  • Anonymous functions called immediately also called The function expression that is called immediately (IIFE), It is similar to a function declaration , By Enclosed in parentheses
;(function () {
  //  Block level scope 
})()
  • ES5 Block level scopes are not yet supported , have access to IIFE simulation
;(function () {
  for (var i = 0; i < 3; i++) {
    console.log(i) // 0、1、2
  }
})()
console.log(i) // ReferenceError: i is not defined,i In the function body scope ( Simulate block level scope ) Inside 
  • ES6 Supports block level scopes , need not IIFE You can achieve the same function
{
  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
  • perform Click handler when , Iterative variable The value of is The final value at the end of the cycle , It can be used IIFE Or block level scope Lock the value to be displayed each time you click
let divs = document.querySelectorAll('div')
for (var i = 0; i < divs.length; ++i) {
  divs[i].addEventListener(
    'click',

    //  The wrong way to write : Print directly ( The value of the click handler iteration variable is the final value at the end of the loop )
    // function(){
    //   console.log(i);
    // }

    //  Correct writing : Immediate function expression , Lock the value to be displayed each time 
    (function (_i) {
      return function () {
        console.log(_i)
      }
    })(i) //  Parameter passes in the value to be displayed each time 
  )
}

for (let i = 0; i < divs.length; ++i) {
  //  use let keyword , Create independent variables for each loop inside the loop 
  divs[i].addEventListener('click', function () {
    console.log(i)
  })
}
  • Empathy , perform Timeout logic when , Iterative variable The value of is The value that causes the loop to exit , It can also be used IIFE Or block level scope Lock the value of each iteration
for (var i = 0; i < 5; i++) {
  //  The timeout logic is executed after exiting the loop , The iteration variable holds the value that causes the loop to exit 5
  setTimeout(() => {
    console.log(i) // 5、5、5、5、5
  }, 0)
}

for (var i = 0; i < 5; i++) {
  //  Function expression with immediate call , Pass in the current index for each loop , Lock the index value that the logic should display for each timeout 
  ;(function (_i) {
    setTimeout(() => {
      console.log(_i) // 0、1、2、3、4
    }, 0)
  })(i)
}

for (let i = 0; i < 5; i++) {
  //  Use let Statement : Declare a new iteration variable for each iteration loop 
  setTimeout(() => {
    console.log(i) // 0、1、2、3、4
  }, 0)
}

10.16 Private variables

  • Any definition in function or In block The variable of , Can be considered as private Of ( Variables in a function or block cannot be accessed outside of it )
  • Private variables include Function parameter local variable and Other functions defined inside the function
function add(num1, num2) {
  // 3 A private variable : Parameters num1、 Parameters num2、 local variable sum
  let sum = num1 + num2
  return sum
}
  • privileged method Is a private variable that can access a function ( And private functions ) The public method of , Can be found in Constructors To realize
function MyObject() {
  let privateVariable = 10
  function privateFunction() {
    console.log('privateFunction')
    return false
  }
  //  privileged method ( Closure ): Access private variables privateVariable And private methods privateFunction()
  this.publicMethod = function () {
    console.log('privateVariable', privateVariable++)
    return privateFunction()
  }
}
let obj = new MyObject()
obj.publicMethod()
/* 
  privateVariable 10
  privateFunction
*/
  • Same as Disadvantages of constructors , The problem with implementing private variables in constructors is : Each instance recreates the method ( Private method & privileged method ), The mechanism is the same Function Object is instantiated multiple times
function Person(name) {
  /*  Private variables name Cannot be accessed directly to , Only through getName() and setName() Privileged methods read and write  */
  this.getName = function () {
    return name
  }
  this.setName = function (_name) {
    name = _name
  }
}
let person = new Person('Nicholas') //  Every time you create an instance, you create a method ( Private method & privileged method )
console.log(person.getName()) // 'Nicholas'
person.setName('Greg')
console.log(person.getName()) // 'Greg'

10.16.1 Static private variable

  • Use Anonymous function expression establish Private scope , Implement privileged methods :

    • Definition Private variables and Private method

      • Private variable as Static private variable , By share , but non-existent In each instance
    • Definition Constructors

      • Use Function expression Define the constructor ( Function declarations create internal functions )
      • Don't use keywords Define the constructor , Make it created in Global scope in
    • Definition Public methods ( privileged method )

      • Defined in the Prototype On
;(function () {
  /*  Anonymous function expression , Create a private scope  */

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

  //  Constructors : Use function expressions  &  Don't use keywords ( Create in global scope )
  MyObject = function () {}

  //  Public methods / privileged method ( Closure ): Defined on the prototype of the constructor 
  MyObject.prototype.publicMethod = function () {
    console.log('privateVariable', privateVariable++)
    return privateFunction()
  }
})()
  • In this way , Private variables and private methods are defined by instances share , privileged method Defined on the prototype , Also by example share
  • Create examples The method... Will not be recreated , but Call the privileged method and modify the static private variable Meeting Affect all instances
;(function () {
  //  Private variables name, Be hidden 
  let name = ''

  //  Constructors , Create in global scope 
  Person = function (_name) {
    name = _name
  }

  //  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', Call the privileged method and modify the static private variable 
console.log(person1.getName()) // 'Michael', Affect all instances 

10.16.2 Module mode

  • stay Singleton object On the basis of , adopt Scope chain Associate private variables with privileged methods :

    • The of the singleton object Object literal Expand to The function expression that is called immediately
    • Inside anonymous functions , Define private variables and private methods
    • Inside anonymous functions , return Contains only properties and methods that can be accessed publicly Object literal
let singleton = (function () {
  /*  The function expression that is called immediately , Create a private scope  */

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

  //  Returns the literal of an object that contains only properties and methods that can be publicly accessed 
  return {
    publicProperty: true,
    publicMethod() {
      console.log(++privateVariable)
      return privateFunction
    },
  }
})()
console.log(singleton) // { publicProperty: true, publicMethod: [Function: publicMethod] }
singleton.publicMethod() // 11
  • The essence On , This pattern defines... With object literals Public interface of singleton object
function BaseComponent() {} // BaseComponent Components 

let application = (function () {
  let components = new Array() //  Create a private array components
  components.push(new BaseComponent()) //  initialization , take BaseComponent Add a new instance of the component to the array 

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

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

function APPComponent() {} // APPComponent Components 
application.registerComponent(new APPComponent()) //  Certified components APPComponent
console.log(application.getComponentCount()) // 2
console.log(application.getRegistedComponents()) // [ BaseComponent {}, APPComponent {} ], Registered component BaseComponent and APPComponent

10.16.3 Module enhancement mode

  • utilize Module mode , stay Before returning the object enhanced , fit A singleton object is an instance of a particular type , But you must add additional properties or methods to it Scene :

    • Inside anonymous functions , Define private variables and private methods
    • Inside anonymous functions , Create sth ( given ) Instance of type
    • Add common properties and methods to the instance object ( enhance )
    • Return instance object
function CustomType() {} //  Specific type 
let singleton2 = (function () {
  //  Private variables and private methods , Be hidden 
  let privateVariable = 10
  function privateFunction() {
    return false
  }

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

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

  //  Return instance 
  return object
})()
console.log(singleton2) // CustomType { publicProperty: true, publicMethod: [Function: publicMethod] }
singleton2.publicMethod() // 11
  • With Module mode Of Singleton object public interface For example , if application Must be BaseComponent Instances of components , Module enhancement mode can be used to create :
let application2 = (function () {
  let components = new Array() //  Create a private array components
  components.push(new BaseComponent()) //  initialization , take BaseComponent Add a new instance of the component to the array 
  let app = new BaseComponent() //  Create a local variable to save the instance 

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

  return app //  Return instance 
})()

console.log(application2) // BaseComponent { getComponentCount: [Function (anonymous)], registerComponent: [Function (anonymous)], getRegistedComponents: [Function (anonymous)] }
console.log(application2.getComponentCount()) // 1
console.log(application2.getRegistedComponents()) // [ BaseComponent {} ], Registered component BaseComponent

application2.registerComponent(new APPComponent()) //  Certified components APPComponent
console.log(application2.getComponentCount()) // 2
console.log(application2.getRegistedComponents()) // [ BaseComponent {}, APPComponent {} ], Registered component BaseComponent and APPComponent
Private variables & Private method privileged method shortcoming
Constructors In the example , Independent In the example Each instance recreates the method ( Private method & privileged method )
Private scope Private scope , static state , share Constructor prototype Call privileged methods to modify private variables , Affect other instances
Module mode Private scope , Independent On a singleton object
Module enhancement mode Private scope , Independent On the instance object

summary & Ask questions

  • What is a closure ? What does it do ?
  • Without using the arrow function ,this When global and local method calls , Point to where ? If it's in an anonymous function this Well ?
  • When functions are nested , How internal functions access external functions this and arguments?
  • What is an immediate function expression ? Please use code to simulate block level scope
  • Please use code to realize the function : Get all div Elements , Click on different div Display its corresponding index value . It is required to use IIFE And block level scope implementation
  • Please use code to realize the function :1 Seconds later 0~4 Numerical iteration of . It is required to use IIFE And block level scope implementation
  • What are private variables ? What might it include ?
  • What is a privileged method ? Please write a code , Implement privileged methods in constructors , And talk about what's wrong with this way
  • Please write a code , Implement privileged methods through private variables , Talk about and prove the limitations of this approach
  • Please write a code , Define the common interface of the singleton object through the module pattern , Realization Web Component registration
  • What scenario is the module enhancement mode suitable for ? Please use code to realize the Web Component registration

copyright notice
author[Xiao Ao],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210823030209929W.html

Random recommended