Home  >  Article  >  Web Front-end  >  Let’s get to know closures together

Let’s get to know closures together

coldplay.xixi
coldplay.xixiforward
2020-09-08 13:25:052597browse

Let’s get to know closures together

Related learning recommendations: javascript video tutorial

Preface

## Closure is always a hurdle that front-end developers cannot get around. Whether you like it or not, you will encounter it at work and in interviews. Everyone's understanding of closure is different. Here I will talk about my own understanding of closure. (If there is any discrepancy with your understanding, please refer to yourself)

How to define closure

Before giving the definition, you may wish to take a look at how others define closure:

Function objects can be related to each other through scope chains, and variables inside the function body can be saved in the function scope. This feature is called "closure" in computer science literature - JavaScript authority Guide (Sixth Edition)

#A closure is a function that has access to a variable in the scope of another function. A common way to create a closure is to create a function inside another function. --Advanced Programming in JavaScript (Third Edition)

A closure occurs when a function can remember and access the lexical scope it is in, even if the function is in the current lexical scope Execution outside the domain. -- JavaScript You Don't Know (Volume 1)

Although the descriptions in the above paragraphs are not the same, you can still find some commonalities after you taste them carefully. The most important of these is the

connection between different scopes. Of course, you can directly quote the above definitions (after all, the above definitions are relatively authoritative). Here, the author prefers the definition in the last paragraph and strongly recommends the book "JavaScript You Don't Know (Volume 1)", which is worth Read carefully and repeatedly.

What knowledge points are involved in closure

It is not enough to just give a definition, you must also explore what knowledge points are involved internally. The following are the knowledge points that the author thinks are useful.

Scope and scope chain

Well, in fact, the author knows that you have all thought of this (no, no one has not thought of this). Now that everyone knows

scope. Here I will briefly describe it and go through the process.

Scope: A set of rules for finding variables by name. Divided into three types: global scope; function scope; block scope.

What needs to be noted is block scope, a new specification in ES6. Variables defined using

let and const inside the curly braces {} will be bound to the scope and cannot be accessed outside the curly braces. Note: There is a temporary dead zone between the beginning of the curly braces and before the let variable declaration (this point is beyond the scope of this article).

Scope chain: When different scopes are trapped together, a scope chain is formed. Note that the search direction is from inside to outside.

Why is the search direction of the scope from inside to outside? This is an interesting question. Personally, I think it is determined by the way in which js execution functions are pushed into the stack (it feels a bit off topic, interested friends can check the information).

Lexical scope

The key point why a function can access variables in another function scope (or remember the current scope and access it outside the current one)

is lexical scope at work. This is very important, but not everyone knows this knowledge point. Let’s briefly discuss it here.

In the programming world, there are two working modes of scope. One is the
lexical scope

used by most programming languages; the other is the opposite. Dynamic Scope (This is beyond the scope of this article).

Lexical scope: The scope of variables and blocks has been determined when you write the code, and will not change with the object or place where it is called (it feels like This is the opposite).

Otherwise, let’s give an example:
let a = 1;
function fn(){
    let a = 2;
    function fn2(){
        console.log(a);
    }
 return fn2;
}

let fn3 = fn();
fn3();

From the above definition, we can know that

fn

is a closure function, fn3 got the pointer address of fn2. When fn3 is executed, fn2 is actually executed, and the a variable inside, According to the search rules of the scope chain, what is found is the variable a in the fn scope, so the final output is 2, not 1. (You can see the picture below)

Let’s get to know closures together
Off topic, how to deceive lexical scope?

Although lexical scope is static, there are still ways to trick it and achieve dynamic effects.

第一种方法是使用eval. eval可以把字符串解析成一个脚本来运行,由于在词法分析阶段,无法预测eval运行的脚本,所以不会对其进行优化分析。

第二种方法是with. with通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。with本身比较难掌握,使用不当容易出现意外情况(如下例子),不推荐使用 -.-

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被泄漏到全局里面去了
// 这是with的一个副作用, 如果当前词法作用域没有该属性,会在全局创建一个

闭包能干啥?

闭包的使用场景可多了,平时使用的插件或者框架,基本上都有闭包的身影,可能您没留意过罢了。下面笔者列举一些比较常见的场景。

  1. 模拟私有变量和方法,进一步来说可以是模拟模块化;目前常用的AMD,CommonJS等模块规范,都是利用闭包的思想;

  2. 柯里化函数或者偏函数;利用闭包可以把参数分成多次传参。如下面代码:

// 柯里化函数
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;
}
  1. 实现防抖或者节流函数;

  2. 实现缓存结果(记忆化)的辅助函数:

// 该方法适合缓存结果不易改变的函数
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;
            return result;
        }
    };
};

如何区分闭包?

说了那么多,我怎么知道自己写的代码是不是闭包呢?先不说新手,有些代码的确隐藏的深,老鸟不仔细看也可能发现不了。 那有没有方法可以帮助我们区分一个函数是不是闭包呢?答案是肯定的,要学会善于利用周边的工具资源,比如浏览器。

打开常用的浏览器(chrome或者其他),在要验证的代码中打上debugger断点,然后看控制台,在scope里面的Closure(闭包)里面是否有该函数(如下图)。

Let’s get to know closures together

闭包真的会导致内存泄漏?

答案是有可能。内存泄漏的原因在于垃圾回收(GC)无法释放变量的内存,导致运行一段时候后,可用内存越来越少,最终出现内存泄漏的情况。常见的内存泄漏场景有4种:全局变量;闭包引用;DOM事件绑定;不合理使用缓存。其中,闭包导致内存泄漏都是比较隐蔽的,用肉眼查看代码判断是比较难,我们可用借助chrome浏览器的Memory标签栏工具来调试。由于篇幅问题,不展开说明了,有兴趣自己去了解一下如何使用。

想了解更多编程学习,敬请关注php培训栏目!

The above is the detailed content of Let’s get to know closures together. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete