1. Scope and scope chain
Scope refers to an area that exists when js variables are used, which is divided into global scope (window) and local scope (function, setTimeout, etc. all generate local scope). When a local-scope variable name duplicates a global-scope variable name, the local variable overrides the global variable.
When using a variable in the local scope, if the corresponding variable cannot be found in its own scope, it will search for the upper-level scope until the global scope. If there is no such variable in the global scope, undefined will be reported. Conversely, variables in the local scope cannot be used in the global scope.
window.a = 1 function(){ // Output 1, although there is no a variable locally, but there is a variable globally. console.log(a) var b = 2 } // An error is reported, local variables cannot be used in the global. console.log(b)
The above process of querying variables layer by layer is called the query scope chain. This structure of layers of local scopes up to the global scope is called a scope chain.
// Global scope, declares a global variable a var a = 100 // Functions generate local scope function acs(){ // declare a local variable b in this local scope var b = 50 // Output: 100, 50 console.log(a, b) // Execution process: find variable a in this scope, // Can't find --> Find it in the upper scope --> Find it globally, use a in the global scope // Find variable b in this scope, found it, use b of this local variable }() // Output: b is not defined console.log(a, b)
2. Closure
1. What is a closure?
A closure refers to a private variable that calls a local variable inside a function outside the function, and the local variable will not be reclaimed by the browser immediately after the call, but will always exist. To put it simply, a function returns a function.
Description in the Red Book: A closure is a function that has access to a variable in the scope of another function.
In fact, the closure is to return a function, and the inclusion relationship formed by the function's reference to the local variable is the closure.
In fact, it is to create a local variable that will not be reclaimed by GC. Because of this, closures have the risk of memory leaks and need to be cleared immediately after each use.
Closure formation: There is a reference to the parent scope in the current environment.
2. How to write closures
// Form closures with self-executing functions var add = function(){ let sum = 0 return function operation(){ return sum = sum ? sum + 1 : 1 } }() // output: 1 add() // output: 2 add() // output: 3 add() // output: 4 add() // Clear closures, delete private variables add = null // output: null console.log(add) // Output: add is not function add()
3. The role of closures
The purpose of using closures - hiding variables, accessing a variable indirectly, calling a function outside the lexical scope where the function is defined.
Closures are often used in callback functions, private properties, and function currying.
4. Use closures to implement multiple image like functions
Completed with closures, multi-picture likes are liked separately, and the number of likes of each input does not interfere with each other. In this example, 5 new independent lexical scopes are declared using closures.
<!-- * @Description: Closure realizes multi-picture likes * @Author: CY small dust s * @Date: 2021-07-28 18:39:33 * @LastEditTime: 2021-11-08 17:19:37 * @LastEditors: Please set LastEditors--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Closure realizes multi-picture likes</title> </head> <style> ul li { list-style: none; float: left; margin: 0px 20px 20px 0px; } ul li img { width: 200px; height: 200px; } </style> <body> <ul> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-0.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-1.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-2.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-3.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-6.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> <li> <img class="img" src="https://www.keaidian.com/uploads/allimg/181206/co1Q206121F4-0-7.jpg" alt="Hello" /> <input type="button" class="add" value="Current number of likes (1)" /> </li> </ul> <script> window.onload = function () { // get ul let add = document.querySelectorAll("ul li input"); // Loop out each input and add the closure for (let i = 0; i < add.length; i++) { // Add a click event to each input add[i].onclick = (function () { let sum = 2; // Return the function, complete the creation of private variables, and form a closure return function () { this.value = "The current number of likes (" + sum++ + ")"; }; })(); } }; </script> </body> </html>
5. Use closures to protect private properties
Create a counter function and define a private property in it, here it is protected from direct modification by a closure.
It can be seen that in this example, we do not operate privatelyCounter directly, but operate privateCounter in the counter through the method actively exposed by makeCounter.
// create a counter const makeCounter = function(){ // Create private variables var privatelyCounter = 0 // output private variable function console(){ return privatelyCounter } // Change counter method function change(num){ privatelyCounter += num } // expose public methods return { CounterAdd(num){ change(num) }, CounterSub(num){ change(num) }, CounterLog(){ return console() } } } // declare two counters const counter1 = makeCounter() const counter2 = makeCounter() counter1.CounterAdd(1) counter1.CounterAdd(1) counter2.CounterSub(-1) counter2.CounterSub(-1) // output: 2 console.log(counter1.CounterLog()) // output: -2 console.log(counter2.CounterLog())
Reference video explanation: into learning
3. Use closures to implement function currying
The so-called function currying is to convert a multi-argument function into a single-argument function.
// normal self-increment method function numAdd(x, y){ return x + y } console.log(numAdd(1, 2)) // Currying with Closures function numAddCurry(x){ return function(y){ return x + y } } // First declare a variable to get the auto-increment method const curry = numAddCurry(1) // When calling this variable for auto-increment, output: 3 console.log(curry(2)) // Or directly call the auto-increment method to pass in two parameters, and the output is also: 3 console.log(numAddCurry(1)(2))