preface

Closures are a constant source of frustration for front-end developers, whether you like them or not, at work and in interviews. Closures vary from person to person, so here’s my take on closures. (If there is any discrepancy with your understanding, please refer to yourself 😶)

How do YOU define closures

Before giving the definition, let’s take a look at how others have defined closures:

Function objects can be related to each other via scope chains, and variables inside the function body can be stored in the function scope, a feature known in the computer science literature as “closures” — JavaScript’s Definitive Guide (6th Edition)

Closures are functions that have access to variables in the scope of another function. A common way to create closures is to create another function inside one function. — JavaScript Advanced Programming (3rd edition)

Closures occur when a function can remember and access its lexical scope, even if the function is executed outside the current lexical scope. JavaScript you Don’t Know (Volume 1)

While the above paragraphs don’t describe the same things, take a closer look and you’ll find some similarities. The most important of these is the relationship between different scopes. Of course, you can use the above definitions (they’re pretty authoritative, after all), but I like the last one, and I recommend JavaScript You Don’t Know (Volume 1), which is worth perusing again and again.

What closures involve

It is not enough to give a definition, but to discuss what is involved internally. Below is the author thinks useful knowledge point.

Scope and scope chain

Well, I know you’ve all thought of that. Now that you know the scope. Here is a brief description, after the end.

Scope: A set of rules for finding variables by name. There are three types: global scope; Function scope; Block scope.

Note the block scope, a new specification in ES6. Variables defined by let inside curly braces {},const, are bound to that scope and cannot be accessed outside curly braces. Note: There is a temporary dead zone (a point beyond the scope of this article) before the curly braces start and the let variable declaration.

Scope chain: A scope chain is formed when different scope traps are mixed together. Notice that the search direction is from the inside out.

Why does the scope look up from inside out? That’s an interesting question. Personally, I think it is decided by the way of loading the js function (feel a bit off topic, interested partners can go to check the information).

Lexical scope

The key to a function’s ability to access a variable in another function’s scope (or to remember the current scope and access it in a place other than the current one) is that lexical scope comes into play. This is an important point, but not everyone knows it, so here’s a brief discussion.

In the programming world, there are two working modes of scope. One is the lexical scope adopted by most programming languages. The other is the opposite dynamic scope (which is beyond the scope of this article).

Lexical scope: The scope of variables and blocks is defined at the time you write code and does not change from object to object or place to place.

Or, here’s an example:


let a = 1;
function fn(){
    let a = 2;
    function fn2(){
        console.log(a);
    }
 return fn2;
}

let fn3 = fn();
fn3();
Copy the code

Fn3 gets the pointer address of fn2. When fn3 executes, it actually executes fn2, and the variable a inside it finds the variable A in FN’s scope according to the search rules of scope chain, so the final output is 2, not 1. (See below)

As an aside, how do I trick the lexical scope?

Although lexical scope is static, there are ways to trick it into being dynamic.

The first method is to use eval. Eval allows you to parse a string into a script to run, but because you cannot predict the script eval is running during the lexical analysis phase, it is not optimized for analysis.

The second method is with. With is often used as a shortcut to refer repeatedly to multiple properties in the same object without having to refer repeatedly to the object itself. With itself is difficult to master and is prone to accidents due to improper use (as shown in the following example). Therefore, -.- is not recommended


function Fn(obj){
	with(obj){
    	a = 2; }}var o1 = {
	a:1
}
var o2 = {
	b:1
}

Fn(o1);
console.log(o1.a); / / 2
Fn(o2);
console.log(o2.a); //undefined;
console.log(a); //2 a is leaked globally
// This is a side effect of with. If the current lexical scope does not have this property, one is created globally

Copy the code

What can closures do?

Closures can be used in many different ways. You may not have noticed that closures can be used in any plug-in or framework you use. Here are some common scenarios.

  1. Simulate private variables and methods, and further, simulate modularity; Currently commonly used AMD,CommonJS module specifications, are the use of closure ideas;

  2. Currization or partial function; Closures can be used to break parameters into multiple passes. Such as the following code:

// Currization function
function currying(fn){
    var allArgs = [];

    function bindCurry(){
        var args = [].slice.call(arguments);
        allArgs = allArgs.concat(args);
        return bindCurry;
    }
    bindCurry.toString = function(){
        return fn.apply(null, allArgs);
    };

    return bindCurry;
}
Copy the code
  1. Realize the anti-shake or throttling function;

  2. Helper functions to cache results (memorization) :

// This method is suitable for caching functions whose results are not easily changed
const memorize = fn= > {
	let memorized = false;
	let result = undefined;
	return (. args) = > {
		if (memorized) {
			return result;
		} else {
			result = fn.apply(null,args); 
			memorized = true;
			fn = undefined;
			returnresult; }}; };Copy the code

How do YOU distinguish closures?

With all that said, how do I know if I’m writing code that’s a closure? Novices aside, some code can be so hidden that even an older bird can’t see it without looking closely. Is there a way to tell if a function is a closure? The answer is yes, learn to make good use of the tools around you, such as the browser.

Open a common browser (Chrome or other), hit the debugger breakpoint in the code you want to validate, and then look at the console to see if the function exists in the Closure within the scope (see figure below).

Do closures really cause memory leaks?

The answer is yes. Memory leaks occur when garbage collection (GC) fails to free the memory of a variable, resulting in less and less available memory after a run, and eventually a memory leak occurs. There are four common memory leak scenarios: global variables; Closure references; DOM event binding; Improper use of cache. Among them, Memory leaks caused by closures are relatively hidden, and it is difficult to judge the code with the naked eye. We can use the Memory TAB bar tool of Chrome browser to debug. Because of the length problem, not to expand the explanation, interested in their own to understand how to use.