preface
Many students have had, when the interview was asked about closure, do not know where to start the situation. In fact, closures are just a phenomenon (or feature) of JS, not as terrible as imagined.
What is a closure
A function and its reference to its lexical environment together constitute a closure. Closures let us access the scope of an external function from an internal function.
So what is a lexical environment?
Lexical environment
Lexical context is understood as the scope context in which a function is defined. Here’s an example:
function a() {
var i = 0;
return function inner() {
i++;
return i;
};
}
// Or so
function a() {
var i = 0;
function inner() {
returni; }}Copy the code
The lexical context of the inner function is the context in which the variable I resides.
A closure is understood to be “closed” in the sense that the scope that the inner function can access is determined when it is declared, not when it is called, and its lexical scope is closed externally.
The “package” of a closure is understood to mean that an inner function keeps references to its lexical environment, as if it were carrying a small backpack containing variables referenced to its lexical environment. When an inner function accesses I and cannot find it in the local scope, it will look in the backpack.
What closures do
-
Closures protect the private variables of a function from external interference
-
Is the variable stored in memory and not destroyed
Here’s an example:
function a() {
var i = 0;
return function () {
i++;
return i;
};
}
var y = a();
y(); / / 1
y(); / / 2
y(); / / 3
Copy the code
The variable I in function A cannot be accessed outside of function A.
- Where is the variable I stored in the closure? Each time you access function y, the variable I is not found in function y, and it is searched up the scope chain until it is not found in the global scope.
Closure application scenarios
Closures are often seen in implementing singleton patterns, currying, shaking, throttling, and modularization
2-0 Singleton mode
The instance is created only once to avoid memory consumption caused by repeated creation
function singleIns(name) {
this.name = name;
}
singleIns.getInstance = (function () {
var instance = null;
return function (name) {
if (!this.instance) {
this.instance = new singleIns(name);
}
return this.instance; }; }) ();var a = singleIns.getInstance("a");
var b = singleIns.getInstance("b");
a === b; // true
Copy the code
2-1 currie
Input parameters can be disassembled and passed in
function curry(func) {
return function curried(. args) {
if (args.length >= func.length) {
func.apply(this, args);
} else {
return function (. args2) {
curried.apply(this, args.concat(args2)); }; }}; }// example
function a(x, y, z) {
console.log(x + y + z);
}
var b = curry(a);
b(1.2.3); / / 6
b(1.2) (3); / / 6
b(1) (2.3); / / 6
b(1) (2) (3); / / 6
Copy the code
2-2 image stabilization
Triggers execution only once for a period of time
// Execute only for the last time
function debounce(func, time) {
let timer = null;
return function (. args) {
timer && clearTimeout(timer);
setTimeout(function () {
func.apply(this, args);
}, time);
};
}
// Execute only the first time
function debounce(func, time){
let timer = null;
func.id = 0;
return function(. args){
if(func.id ! = =0) {clearTimeout(timer;)
}
timer = setTimeout(function () {
func.id = 0;
func.apply(this, args);
}, time);
func.id = func.id + 1; }}Copy the code
2-3 throttling
It fires over a period of time, at regular intervals
// Timestamp mode
function throttle(func, time) {
let start = new Date(a);return function (. args) {
if (new Date() - start >= time) {
func.apply(this, args);
start = new Date();
}
};
}
Copy the code
2-4 modular
Modular encapsulation encapsulates internal logic inside a module and exposes methods outside the module.
function module() {
var pool = [];
function add(item) {
pool.push(item);
return pool;
}
function remove(num) {
pool.forEach((item, index) = > {
if (item === num) {
pool.splice(index, 1); }});return pool;
}
return {
add: add,
remove: remove,
};
}
var foo = module(a); foo.add(); foo.remove();Copy the code
Is it easy to understand closures by looking closely at the above example?
The downside of closures
Overuse of closures can cause memory leaks (where useless variables are stored in memory and cannot be reclaimed, constantly occupying memory). The solution to this problem is to clear the variable (set to null).
function a() {
var i = 0;
return function () {
i++;
return i;
};
}
var y = a();
y();
y = null; // After the variable is cleared, the reference disappears and the closure ceases to exist
Copy the code
Frequent meeting questions
-
- Look at the output
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
Copy the code
The output is: Five consecutive fives. Modify the preceding code to output 0, 1, 2, 3, and 4
Method 1: Use the third argument of setTimeout
for (let i = 0; i < 5; i++) {
setTimeout(
function () {
console.log(i);
},
0,
i
);
}
Copy the code
Method two: Use let
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
Copy the code
Let creates a block-level scope.
Method three: Use self-executing functions
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 0);
})(i);
}
Copy the code
The self-executing function passes I into a new execution context.
Method four: Use function arguments
function output(i) {
setTimeout(function () {
console.log(i);
}, 0);
}
for (var i = 0; i < 5; i++) {
output(i);
}
Copy the code
Function parameters are passed by value.
Method five: borrow async
const timeout = function (time) {
return new Promise((resolve, reject) = > {
setTimeout(function () {
resolve();
}, time);
});
};
async function print() {
for (var i = 0; i < 5; i++) {
await timeout(1000);
console.log(i); }}await print();
Copy the code
- Look at the output
var a = 100;
function create() {
var a = 200;
return function () {
console.log(a);
};
}
var fn = new create();
fn();
Copy the code
This problem is very simple, is the concept of closure, output 200, I won’t explain here.
- Implement a sum(x)(y) function that does the same thing as sum(x,y)
function sum(a) {
return function (b) {
return a + b;
};
}
sum(1) (2); / / 3
Copy the code
We’re looking at the concept of Curryization
tips
Do all closures need to be manually cleared?
Closures that are useful do not need to be cleared, and closures that are not useful need to be cleared. If it is a closure in a DOM event, components in vue and React have a life cycle. When uninstalled, events bound to the DOM will be automatically removed, and memory will be automatically reclaimed.
Do not use closures during development?
Closures are everywhere, and have been created every time we simulate private variables. Don’t keep long objects and arrays in memory; use closures wisely.
How do I check for memory leaks?
Closures are not the only cause of memory leaks, and the Perfomance and Memory tools of The Chrome Developer Tools (devTools) allow you to look at memory in detail. This article is not about memory leaks, so I don’t want to go into details about how to use devTools.
conclusion
Closure as one of the front-end eight-part essay, baffled a lot of students are looking for a job. Little sister is also in a learning process, there may be inaccurate, incorrect place, welcome everyone to like, discuss, progress together ah. I hope that’s helpful to some of you.
This article is a series of [JS Foundation] articles, pay attention to the little sister, learn a study together.