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 closureuse
this
It makes your code more complicated. If the inner function doesn’t use the arrow function,this
Binding to theThe context in which the function is executed:- Called in a global function, not in strict mode
this
Point to thewindow
, the strict mode is equal toundefined
- As a method call on an object,
this
Point to that object - Anonymous functionsDoes not bind to an object, which
this
Point to theThe object that calls the anonymous function
- Called in a global function, not in strict mode
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 called
this
andarguments
.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 cases
this
value
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
- definePrivate variablesandPrivate methods
; (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,
application
It must beBaseComponent
An 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