zoukankan      html  css  js  c++  java
  • JS闭包的理解及常见应用场景

    JS闭包的理解及常见应用场景

    一、总结

    一句话总结:

    闭包是指有权访问另一个函数作用域中的变量的函数

    1、如何从外部读取函数内部的变量,为什么?

    闭包:f2可以读取f1中的变量,只要把f2作为返回值,就可以在f1外读取f1内部变
    原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
    function f1(){
            var n = 123;
            function f2(){    //f2是一个闭包
                alert(n)
            }    
            return f2;
        }
    
    js链式作用域:子对象会一级一级向上寻找所有父对象的变量,反之不行。

    2、js链式作用域?

    子对象会一级一级向上寻找所有父对象的变量,反之不行。
    js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。

    3、js变量两种作用域?

    全局变量、局部变量(函数内):js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。

    4、闭包为什么可以实现在函数外读取到函数内的变量?

    |||-begin

    function f1(){
            var n = 123;
            function f2(){    //f2是一个闭包
                alert(n)
            }    
            return f2;
        }

    |||-end

    原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。

    二、对JS闭包的理解及常见应用场景

    转自或参考:对JS闭包的理解及常见应用场景
    https://blog.csdn.net/qq_21132509/article/details/80694517

     

    1、变量作用域

    变量作用域两种:全局变量、局部变量。js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。
    

    2、如何从外部读取函数内部的变量?

    function f1(){
            var n = 123;
            function f2(){    //f2是一个闭包
                alert(n)
            }    
            return f2;
        }
    js链式作用域:子对象会一级一级向上寻找所有父对象的变量,反之不行。
    f2可以读取f1中的变量,只要把f2作为返回值,就可以在f1外读取f1内部变量
    

    3、闭包概念

    能够读取其他函数内部变量的函数。
    或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。
    

    4、闭包用途

    1、读取函数内部的变量
    2、让这些变量的值始终保持在内存中。不会再f1调用后被自动清除。
    3、方便调用上下文的局部变量。利于代码封装。
    原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
    

    5、闭包理解

    /**
     * [init description]
     * @return {[type]} [description]
     */
    function init() {
        var name = "Chrome";    //创建局部变量name和局部函数alertName
    
        function alertName() { //alertName()是函数内部方法,是一个闭包
            alert(name); //使用了外部函数声明的变量,内部函数可以访问外部函数的变量
        }
        alertName();
    }
    init();
    //一个变量在源码中声明的位置作为它的作用域,同时嵌套的函数可以访问到其外层作用域中声明的变量
    
    /**
     * [outFun description]
     * @return {[type]} [description]
     */
    function outFun(){
        var name = "Chrome";
        function alertName(){
            alert(name);
        }
        return alertName;   //alertName被外部函数作为返回值返回了,返回的是一个闭包
    }
    
    var myFun = outFun();
    myFun();
    /*
    闭包有函数+它的词法环境;词法环境指函数创建时可访问的所有变量。
    myFun引用了一个闭包,闭包由alertName()和闭包创建时存在的“Chrome”字符串组成。
    alertName()持有了name的引用,
    myFunc持有了alertName()的的访问,
    因此myFunc调用时,name还是处于可以访问的状态。
     */
    
    
    /**
     * [add description]
     * @param {[type]} x [description]
     */
    function add(x){
        return function(y){
            return x + y;
        };
    }
    
    var addFun1 = add(4);
    var addFun2 = add(9);
    
    console.log(addFun1(2)); //6
    console.log(addFun2(2));  //11
    //add接受一个参数x,返回一个函数,它的参数是y,返回x+y
    //add是一个函数工厂,传入一个参数,就可以创建一个参数和其他参数求值的函数。
    //addFun1和addFun2都是闭包。他们使用相同的函数定义,但词法环境不同,addFun1中x是4,后者是5
    

    6、闭包应用场景之setTimeout

    
        //原生的setTimeout传递的第一个函数不能带参数
        setTimeout(function(param){
            alert(param)
        },1000)
    
    
        //通过闭包可以实现传参效果
        function func(param){
            return function(){
                alert(param)
            }
        }
        var f1 = func(1);
        setTimeout(f1,1000);
    

    7、闭包应用场景之回调

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <link rel="stylesheet" href="">
    </head>
    <style>
        body{
            font-size: 12px;
        }
        h1{
            font-size: 1.5rem;
        }
        h2{
            font-size: 1.2rem;
        }
    </style>
    <body>
    
        <p>哈哈哈哈哈哈</p>
        <h1>hhhhhhhhh</h1>
        <h2>qqqqqqqqq</h2>
    
        <a href="#" id="size-12">12</a>
        <a href="#" id="size-14">14</a>
        <a href="#" id="size-16">16</a>
    
    <script>
        function changeSize(size){
            return function(){
                document.body.style.fontSize = size + 'px';
            };
        }
    
        var size12 = changeSize(12);
        var size14 = changeSize(14);
        var size16 = changeSize(16);
    
        document.getElementById('size-12').onclick = size12;
        document.getElementById('size-14').onclick = size14;
        document.getElementById('size-16').onclick = size16;
        //我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
    </script>
    </body>
    </html>

    8、闭包应用场景之封装变量

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>闭包模拟私有方法</title>
        <link rel="stylesheet" href="">
    </head>
    <body>
    <script>
        //用闭包定义能访问私有函数和私有变量的公有函数。
        var counter = (function(){
            var privateCounter = 0; //私有变量
            function change(val){
                privateCounter += val;
            }
            return {
                increment:function(){   //三个闭包共享一个词法环境
                    change(1);
                },
                decrement:function(){
                    change(-1);
                },
                value:function(){
                    return privateCounter;
                }
            };
        })();
    
        console.log(counter.value());//0
        counter.increment();
        counter.increment();//2
        //共享的环境创建在一个匿名函数体内,立即执行。
        //环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。
    
    </script>
    </body>
    </html>

    9、闭包应用场景之为节点循环绑定click事件

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <link rel="stylesheet" href="">
    </head>
    <body>
    
        <p id="info">123</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>
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                var item = infoArr[i];
                document.getElementById(item.id).onfocus = function(){
                    showContent(item.content)
                }
            }
        }
        setContent()
        //循环中创建了三个闭包,他们使用了相同的词法环境item,item.content是变化的变量
        //当onfocus执行时,item.content才确定,此时循环已经结束,三个闭包共享的item已经指向数组最后一项。
    
    
    
        /**
         * 解决方法1     通过函数工厂,则函数为每一个回调都创建一个新的词法环境
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function callBack(content){
            return function(){
                showContent(content)
            }
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                var item = infoArr[i];
                document.getElementById(item.id).onfocus = callBack(item.content)
            }
        }
        setContent()
    
        /**
         * 解决方法2        绑定事件放在立即执行函数中
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                (function(){
                    var item = infoArr[i];
                    document.getElementById(item.id).onfocus = function(){
                        showContent(item.content)
                    }
                })()//放立即执行函数,立即绑定,用每次的值绑定到事件上,而不是循环结束的值
            }
        }
        setContent()
    
        /**
         * 解决方案3        用ES6声明,避免声明提前,作用域只在当前块内
         */
        function showContent(content){
            document.getElementById('info').innerHTML = content;
        };
    
        function setContent(){
            var infoArr = [
                {'id':'email','content':'your email address'},
                {'id':'name','content':'your name'},
                {'id':'age','content':'your age'}
            ];
            for (var i = 0; i < infoArr.length; i++) {
                let item = infoArr[i];      //限制作用域只在当前块内
                document.getElementById(item.id).onfocus = function(){
                    showContent(item.content)
                }
            }
        }
        setContent()
    </script>
    </body>
    </html>
     
  • 相关阅读:
    vue项目按钮权限配置
    页面框架搭建模板可复用
    vue循环渲染复选框列表
    Vue项目网页版在浏览器中实现扫码识别功能(项目应用篇)
    基于51单片机的智能小车
    卡特兰数:翻折思想 翻折前后一一对应
    Redis之Info指令
    Redis的安全使用
    Redis中的近似LRU超出内存限制后的处理
    Redis主从同步
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/11590231.html
Copyright © 2011-2022 走看看