[toc]
Closures are a difficult and characteristic feature of Javascript, and many advanced applications rely on closures.
Here are my study notes, for Javascript beginners should be very useful.
I. Introducing closures
Need: Click on a button and say “click on the NTH button”
<button> Test 1</button> <button> Test 2</button> <button> Test 3</button> <! - requirements: Click on a button, Prompt "click is the NTH button" -- -- > < script type = "text/javascript" > var BTNS = document. The getElementsByTagName (' button ') / / traversal and listening for (var I = 0, length = btns.length; i < length; I++) {var BTN = BTNS. [I] BTN onclick = function () {alert (' first '+ (I + 1) +' a ')}} < / script >Copy the code
Why is the last button clicked instead of the corresponding one?
1.1 Solution 1: Save the subscript corresponding to the BTN on the BTN
for (var i = 0, length = btns.length; i < length; Var btn.index = btn.onclick = function () {alert(' '+ (this.index + 1) +) {var BTN = BTNS [I] // BTN = btn.onclick = function () {alert('' + (this.index + 1) + 'a ')}}Copy the code
1.2 Solution 2 :==Use a closure= =
for (var i = 0, length = btns.length; i < length; I++) {/ / immediate execution function (function (j) {var BTN = BTNS BTN [j]. J onclick = function () {alert (' first '+ (j + 1) +' a ')}}) (I)}Copy the code
1.3 Solution 3: ES6 new syntax: let keyword plus code block
for (let i = 0, length = btns.length; i < length; i++) { let btn = btns[i]; Btn.onclick = () => {alert(' ${I +1} '); }}Copy the code
The concept of closures
2.1 Official interpretation of MDN
In human language, as long as a function internally references external data, it forms a closure. Such as:
function f1(){
const a = 10
function f2(){
console.log(a) // f2 refers to f1 data A, forming a closure}}Copy the code
One of the benefits of closures is that you can safely cache data so that you can use it at certain times.
2.2 Summary of closure understanding
1. How do closures come about?
Closure == is generated when a nested inner (child) function references a variable (function) of the nested outer (parent) function
2. What exactly is a closure?
Use Chrome debug to view
Understanding 1: Closures are nested internal functions (most people)
Understanding 2: Objects that contain referenced variables (functions) (very few people)
Note: Closures exist in nested inner functions
3. Conditions that generate closures?
Nested function
An inner function references data from an external function (variable/function).
Function fn1() {var a = 2 var b = 'ABC' function fn2() {console.log(a)} // fn2()} fn1()Copy the code
function fun1() {
var a = 3
var fun2 = function () {
console.log(a)
}
}
fun1()
Copy the code
The function of closures
Closure functions:
- == Use variables inside a function that live in memory after the function is executed (extending the life of local variables)==
- == allows the outside of the function to manipulate (read and write) data (variables/functions) inside the function ==
Closure problems:
-
Do local variables declared inside the function still exist after the function is executed? In general, it does not exist, only variables that exist in a closure can exist
-
Can local variables inside a function be accessed directly from outside the function? No, but we can let the outside manipulate it through closures
Life cycle of closure
-
Generated: generated when the nested inner function definition is finished executing (not called)
-
Death: when nested internal functions become garbage objects
Function fn1() {// The closure has been generated (function promotion, Var a = 2 function fn2() {a++ console.log(a)} return fn2} var f = fn1() f() // 3 f() // 4 f = null // Closure dies (function objects containing closures become junk)Copy the code
The principle of closures
5.1 Scope and scope chain
The reason why closures do this is that there are scopes and scope chains in JS.
1. Scope:
The valid scope of a variable or function. A variable or function can only be used within a certain scope. We call this scope.
2. Scope chain:
Multiple scopes are nested with each other to form certain access rules, which are called scope chains
- When accessing data (a variable or function) in the current scope, use the data in the current scope first if it exists in the current scope
- If the data does not exist in the current scope, it is searched up the chain of scopes
- If the global use domain is not found, an error is reported
Closures consist of two multilevel scopes. Data is declared in a local scope, so that it is accessible only from within that scope. So we declare the function inside the use field, and the inner function can access the data of the outer function. If we want to manipulate the local data, we send a function back, receive the function externally, and then we can manipulate the internal data externally with the internal function.
Application of closures
- Loop over listening: add click listening to multiple Li’s to read the current subscript
- Modularity: Encapsulate some data and the functions that manipulate it, exposing some behavior
- The JS framework (jQuery) makes extensive use of closures
6.1 Closure Application 1: Defining JS modules (writing method 1)
Closure applications 2: Defines a JS module * a JS file with a specific function * an object or function that encapsulates all data and functionality within a function (private) * exposes only a package of n methods * a user of the module, Function myModule() {var MSG = 'My atguigu' // function doSomething() { console.log('doSomething() '+msg.toUpperCase()) } function doOtherthing () { console.log('doOtherthing() '+ msg.tolowerCase ())} return {doSomething: doSomething, doOtherthing: doOtherthing } } var module = myModule() module.doSomething() module.doOtherthing()Copy the code
6.2 Closure Application 2: Defining JS modules (Writing method 2)
(function () {var MSG = 'My atguigu' function doSomething() {console.log('doSomething() ') '+ msg.toupperCase ())} function doOtherthing() {console.log('doOtherthing() '+ msg.tolowercase ())} window.myModule2 = { doSomething: doSomething, doOtherthing: doOtherthing } })()Copy the code
myModule2.doSomething()
myModule2.doOtherthing()
Copy the code
6.3 Simulated case of bank deposit and withdrawal
For example, if we want to make a deposit and withdraw money effect in a bank, it would be very dangerous to expose the data directly
// The money you have in the bank
let money = 0
/ / save
function add(n){
money += n
}
/ / make a withdrawal
function reduce(n){
money -= n
}
Copy the code
At this point, we have implemented a data in the global usage domain, and we can manipulate it in the console at will
We’re going to have to privatize this data, wrap it in a function
function f(){
let money = 0
function add(n){
money += n
}
function reduce(n){
money -= n
}
}
Copy the code
Money is a local variable that can only be manipulated internally, while the Add and reduce functions form closures.
But in general it doesn’t make sense to write this because we can’t operate on money, so we want to operate on money outside f by adding and reducing as the return values of f
So that we can see the current value of money, we add a function to the returned data that the user uses to check the balance
function f(){
let money = 0
function add(n){
money += n
}
function reduce(n){
money -= n
}
function query(){
console.log(money)
}
return {add,reduce,query}
}
Copy the code
Now, if we call it, we’ll get an object, and that object’s methods can handle money, but nothing else
let bank = f()
bank.add(100) // money:100
bank.reduce(100) // money: 0
Copy the code
Along these lines, we simulate banks using closures:
function f(){
let vault = {} // a vault that stores all people's money, using the name as the key and the amount as the value
// Name is the user name and value is the money to be saved
function add(name,money){
if(typeofmoney ! = ='number' && Object.is(money,NaN) && money <=0) {return console.error("Please enter the correct amount")}if(! vault[name]) vault[name] =0
vault[name] += money
}
function reduce(name,money){
if(! (namein vault)) return console.error('There is no such user')
if(typeofmoney ! = ='number' && Object.is(money,NaN) && money <=0)
return cosole.error('Please enter the correct amount')
if(money > vault[name]) return console.error('You don't have that kind of money')
vault[name] -= money
}
function query(name){
console.log(vault[name])
}
return {add,reduce,query}
}
Copy the code
Disadvantages and solutions of closures
7.1 Memory Overflow and leakage
7.1.1 Memory Overflow
A program error that throws out of memory when the program needs more memory to run than is availableCopy the code
7.1.2 Memory Leaks
Common memory leaks: unexpected global variables that do not clean up timers or callback function closures in timeCopy the code
7.2 Disadvantages of closures
- After the function is executed, local variables in the function are not released, and the memory usage becomes longer
- Closures are prone to memory leaks
7.3 Closure Problem Solved
- Don’t use closures if you can
- F = null; // make the internal function object junk
function fn1() { var arr = new Array[100000] function fn2() { console.log(arr.length) } return fn2 } var f = fn1() f() f = null // make internal functions garbage objects --> reclaim closureCopy the code
7.4 Memory Leakage Example Analysis
Closures can cache data safely, but they also pose some problems. Because the closure references data from external functions, external functions are unable to reclaim data at the end of the run, which always occupies the memory of the program and causes the program to run slowly. ———— We call data that cannot be reclaimed a memory leak
As to why memory leaks occur, we need to have a general understanding of the garbage collection mechanism
In simple terms, each piece of data has a token that is used, and each time a place uses the data, the number of tokens increases by 1, and when the token reaches 0, it goes into a state waiting for collection.
function fn(){
var a = 10
console.log(a)
}
fn()
Copy the code
In the above code, when we call fn, we will use a in line 3, the number of references +1, after use, the number of references -1, this time will be 0, so when fn call is completed, the data a will enter the recycling process.
But if we write code like this
function fn(){
let a = 10
function f2(){
console.log(f2)
}
}
let f = fn()
Copy the code
When FN was executed, it was found that A was used in F2, and the reference was +1. Originally, F2 would also be recycled after FN was finished, but it was found that there was an F outside that saved the reference of F2, and the reference of F2 was +1, so F2 could not be recycled, and a could not be recycled, thus causing a memory leak.
If we do not need to use the closure, we can manually release the reference
f = null
Copy the code
At this point, F will no longer reference F2, f2 reference -1, f2 reference is 0, when F2 is recycled, there is no other place to use A, A reference -1, so a reference also becomes 0, and finally A is recycled.
So when you use closures, it’s important to note that :== because not all the time we notice when the closure is no longer used, it’s released manually. So don’t use closures if you can. = =
Closure interview questions
8.1 Interview Question 1:
Var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; }}; alert(object.getNameFunc()()); / /? Var name2 = "the window "; var object2 = { name2: "My Object", getNameFunc: function () { var that = this; return function () { return that.name2; }; }}; alert(object2.getNameFunc()()); / /? my objectCopy the code
8.2 Interview Question 2:
function fun(n, o) { console.log(o) return { fun: function (m) { return fun(m, N)}}} var a = fun (0) a.f UN a.f (1) the UN a.f (2) the UN (3) / / undefined, 0, 0, var b = fun (0) fun (1) fun (2) fun (3) Var c = fun(0).fun(1) c.sun (2) c.sun (3) //undefined,0,1,1Copy the code