• 【js基础修炼之路】— 深入浅出理解闭包


    之前对于闭包的理解只是很肤浅的,只是浮于表面,这次深究了一下闭包,下面是我对闭包的理解。

    什么是闭包?

    引用高程里的话 =>
    闭包就是有权访问另一个作用域中变量的函数,闭包是由函数以及创建该函数的词法环境组成而成
    记住,闭包是一个函数,废话不多说,先来个例子:

    function makeFunc() {
        var name = "Mozilla";
        function displayName() {
            alert(name);
        }
        return displayName;
    }
    
    var myFunc = makeFunc();
    myFunc();
    

    函数makeFunc里面定义了一个函数,该函数引用了一个makeFunc内部的变量,然后返回该函数,myFunc变量承接了makeFunc返回的函数,也就是
    function displayName() { alert(name); }

    在下面执行该函数,咦!!!为什么函数里面的name是从哪里得到的,为什么不报错呢?
    要搞明白其中的细节,必须从理解函数被调用的时候都会发生什么入手

    那么什么是作用域链呢?

    当一个函数被调用的时候,会创建一个执行环境(执行上下文),以及相应的作用域,在函数执行完的时候该作用域链会被销毁,函数里面的变量也会被回收,但是闭包的情况不是这样的..
    image.png
    还是上面的例子,打印了一下myFunc函数,发现该函数的作用域链(scopes)保存着name变量,name变量并没有被回收,于是我们成功的在makeFunc函数外的作用域取到了name变量,myFunc就是闭包,我们重温一下闭包的特点=>闭包是由函数以及创建该函数的词法环境组成而成.
    这下概念就理解的更清楚了吧!

    闭包与变量

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    
    <body>
        <p id="help">Helpful notes will appear here</p>
        <p>E-mail: <input type="text" id="email" name="email"></p>
        <p>Name: <input type="text" id="name" name="name"></p>
        <p>Age: <input type="text" id="age" name="age"></p>
        <script type="text/javascript">
        function showHelp(help) {
            document.getElementById('help').innerHTML = help;
        }
        var helpText = [
            { 'id': 'email', 'help': 'Your e-mail address' },
            { 'id': 'name', 'help': 'Your full name' },
            { 'id': 'age', 'help': 'Your age (you must be over 16)' }
        ];
    
        function returnHelp(i) {
            return helpText[i];
        }
    
        function setupHelp() {
            for (var i = 0; i < helpText.length; i++) {
                var item = returnHelp(i);
                document.getElementById(item.id).onfocus = function() {
                    showHelp(item.help);
                }
            }
        }
        setupHelp(); 
        </script>
    </body>
    
    </html>
    

    本来想实现一个点击输入框,显示提示信息的功能,但是结果是显示的全是age信息。。。为什么???

    原因是赋值给onfouse的是闭包,这些闭包是由他们的函数定义和在setupHelp作用域中捕获的环境所组成的 这三个闭包在循环中被创建,但他们共享同一个词法作用域,在这个作用域中存在一个变量item, 点击输入框的时候,item已经变成helpText 的最后一个了,所以。。。
    怎么解决呢?
    既然聚焦输入框后的回调函数里面的item变量引用的是共享的,所以只要把他变成非共享的就可以了,

     function showHelp(help) {
            document.getElementById('help').innerHTML = help;
        }
        var helpText = [
            { 'id': 'email', 'help': 'Your e-mail address' },
            { 'id': 'name', 'help': 'Your full name' },
            { 'id': 'age', 'help': 'Your age (you must be over 16)' }
        ];
    
        function returnHelp(i) {
            return helpText[i];
        }
    
        function setupHelp() {
            for (var i = 0; i < helpText.length; i++) {
                var item = returnHelp(i);
                //通过这种方法把每一个item变成具体的实例,而不是变量,避免每一次聚焦的时候执行函数里面的变量都是指向同一个数
                document.getElementById(item.id).onfocus = change(item);
            }
        }
    
        function change(item) {
            return function() {
                showHelp(item.help);
            }
        }
        setupHelp();
    

    这样把每一次循环的item传给外面的函数,然后外边函数返回具体的实例而非变量,这样就解决了问题。

    下面再来看一个经典的例子 :

    <html>
        <body>
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
        <script>
            var nodes = document.getElementsByTagName('div');
            for(var i = 0, len = nodes.length; i < len; i++){
                nodes[i].onclick = function(){
                    alert(i);
                }
            };
        </script>
      </body>
    </html
    

    如果你认为点击每一个div会返回对应的索引,那么恭喜你,你掉坑了!!!
    没错,一开始我也掉坑了,可能大家会感到很疑惑,为什么点完全是5,因为点击事件是异步的,点击的时候变量 i 已经变成5了,所以点击的时候弹出的全是5,怎么解决呢,闭包来了

    for(var i = 0, len = nodes.length; i < len; i++){
        (function(i){       
            nodes[i].onclick = function(){
                    console.log(i);
                }
            })(i)
    };
    

    这段代码是如何完美的解决问题的呢?
    当我们点击的时候,回调函数里面的 i 引用的是外面闭包的 i ,这样问题就被完美的解决了。

    闭包的缺点

    在上面分析的过程中也说到过,闭包不会被垃圾回收机制回收,会造成内存泄漏,记得闭包使用完手动把变量回收一下。

    记住,在你成功之前,每前进一步遇到的都将是更大的困难,所以微笑面对每一个困难 -----至有理想的自己
  • 相关阅读:
    史上最简单的Hibernate入门简单介绍
    极客Web前端开发资源大荟萃
    ios7 UIScrollView 尺寸问题
    用ahk脚本自己主动删除flashcookies
    CSS文字样式
    Windows 10 安装
    万圣节福利:红孩儿3D引擎开发课程《3ds max导出插件初步》
    算法实验 层序列表问题(二叉树)
    git配置别名
    git忽略特殊文件
  • 原文地址:https://www.cnblogs.com/-yu-ze-/p/10208686.html
Copyright © 2020-2023  润新知