zoukankan      html  css  js  c++  java
  • 浅谈-闭包

    前情纪要:上次的公司例会上的分享会上我准备了一个关于“闭包”的ppt,然后自己向部门的同事们讲解了一下我对于这部分内容的理解,但是讲完了之后发现自己还有很多地方说的不是很透彻。


    一:背景知识小解:

    这里要讲的闭包其实是“javascript 中的闭包”要理解闭包的概念首先要知道一些关于js(javascript的简称,下面就js)的基础知识

    Javascript特殊的变量作用域。

    变量的作用域无非就是两种:全局变量和局部变量。

    Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

    另一方面,在函数外部自然无法读取函数内的局部变量。

    介绍到这里可能有人要问,为啥不能读取

    这就是Javascript语言特有的“链式作用域”结构chain scope),

    子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,

    对子对象都是可见的,反之则不成立。

    垃圾回收机制(garbage collection:垃圾收集器会定期(周期性)找出

    那些不在继续使用的变量,然后释放其内存。

    不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,

    全局变量的生命周期直至浏览器卸载页面才会结束。


    二:初始闭包:

    这就是最简答的一个“闭包”,

    函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。

    编程界崇尚以简洁优雅唯美,很多时候 如果你觉得一个概念很复杂,那么很可能是你理解错了。

    还要稍微复杂一点的闭包:


    三:如何理解闭包:

    闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。 假设我们在做一个游戏,在写其中关于「还剩几条命」的代码。 如果不用闭包,你可以直接用一个全局变量: window.lives = 30 // 还有三十条命 这样看起来很不妥。万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人 「直接访问」这个变量。怎么办呢? 用局部变量。 但是用局部变量别人又访问不到,怎么办呢? 暴露一个访问器(函数),让别人可以「间接访问」。那么在其他的 JS 文件,就可以使用 window.奖励一条命() 来涨命,使用 window.死一条 命() 来让角色掉一条命。

    其实我觉得闭包就是这样一个很简单的概念,这种概念不光光是应用在js中,

    PHPScalaSchemeCommon LispSmalltalkGroovyJavaScriptRubyPythonGoLuaobjective cswift 以及JavaJava8及以上)等语言中都能找到对闭包不同程度的支持。

    所以说对于技术来说,基本的原理都是相同的,就像现在的js中有很多在使用面向对象的思路在写框架、写接口、写插件。


    四:闭包的几种表现形式

    1、匿名自执行函数

    var data= {    
        table : [],    
        tree : {}    
    };    
         
    (function(dm){    
        for(var i = 0; i < dm.table.rows; i++){    
           var row = dm.table.rows[i];    
           for(var j = 0; j < row.cells; i++){    
               drawCell(i, j);    
           }    
        }    
           
    })(data);   
    ( function() {}() );
    ( function() {} )();
    [ function() {}() ];
    
    ~ function() {}();
    ! function() {}();
    + function() {}();
    - function() {}();
    
    var f = function() {}();
    下面再讲-

    2:结果缓存

    3:封装

    4:继承

    简单讲一下3:封装

    var person = function(){    
        //变量作用域为函数内部,外部无法访问    
        var name = "default";       
        return {    
           getName : function(){    
               return name;    
           },    
           setName : function(newName){    
               name = newName;    
           }    
        }    
    }();    
         
    alert(person.name);//直接访问,结果为undefined    
    alert(person.getName());    
    person.setName("abruzzi");    
    alert(person.getName());   (演示一下吧!)
       
    得到结果如下:  
    undefined  
    default  
    abruzzi

    小总结:学习过有关“面向对象“原理的高级语言的同学应该看出来了,这就是面向对象的思想,所以再次强调,技术是相同的


    五:闭包的优缺点

    优点:

    1:可以读取函数内部的变量,

    2:让这些变量的值始终保持在内存中,

     缺点:

    1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。--但是在这里有个疑问:究竟怎么定义内存泄漏,要是用的内存、变量还叫做泄漏吗?

    2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。


    六:一点延伸:什么是函数申明、函数表达式

    function fnName () {…
    };  
    
     var fnName = function () {
    …};

    函数声明:function fnName () {…}; 使用function关键字声明一个函数,再指定一个函数名,叫函数声明。

    函数表达式: var fnName = function () {…}; 使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。

    匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。

    函数声明和函数表达式不同之处在于,一、Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用

    总结:函数声明提升是重点,能理解了这个概念就懂了声明和表达式的区别了。


    七:关于这次分享的原因

    引发这次技术学习的一段代码我贴出来

            var Eplus365Verifiy = (function (self) {
                self.FrameworkName = 'Eplus365Verifiy.js';
                self.FrameworkVersion = '1.0.0';
                //手机号的正则验证
                self.MobileVerifiy = function (val) {
                    var myreg = /^1[3,4,5,7,8]d{9}$/;
                    if (myreg.test(val)) {
                        return true;
                    }
                    return false;
                }
                //长度验证,区分中文和字符-如果数据类型设置为varchar才需要这方法验证,如果是nvarchar就不要用了
                self.LengthVerifiy = function (val, length) {
                    var len = getStringLen(val);
                    if (len > length) {
                        return false;
                    } else {
                        return true;
                    }
                }
                return self;
            } (Eplus365Verifiy || {}));
    
            var Eplus365Verifiy = (function (self) {
                self.ss = "ss";
                return self;
            } (Eplus365Verifiy || {}))
    
                console.log(Eplus365Verifiy.MobileVerifiy("15235382691"));
                console.log(Eplus365Verifiy.ss);

    这里需要理解的是

    1:匿名自执行函数

    2:Eplus365Verifiy || {} 没有值得时候的初始化

    3:return self ;//把本身作为一个返回值,而不是方法。

    4:两次定义,但是给同意对象。


    总结:

    这次关于闭包的技术分享,其实更多的是关于js基础知识点:变量作用域、链式作用域、垃圾回收机制、函数声明、函数表达式、匿名自执行函数等等概念的理解,闭包是一个知识点,很简单但也不是很好理解,希望对大家有帮助吧。

    这次学习参考了很多技术大牛的经验,再次鸣谢。

  • 相关阅读:
    hadoop 伪分布式执行 mapreduce 任务时报 running beyond physical memory或者beyond vitual memory limits
    hadoop 伪分布式 yarn resourcemanager 无法启动
    ESP-ADF相关学习笔记
    ESP32 ADF windows开发环境搭建 适配ADF到ESP32A1S(转)
    ESP32音频输入-MAX4466,MAX9814,SPH0645LM4H,INMP441(翻译)
    itest(爱测试)开源接口测试&敏捷测试&极简项目管理 6.6.6 发布,新增接口mock
    开源一站式敏捷测试管理,极简项目管理平台 itest(爱测试) 6.6.2 发布,便捷迫切功能增强
    开源一站式敏捷测试管理平台 itest(爱测试) 6.6.1 发布,安全升级及新增强
    开源一站式敏捷测试管理&极简项目管理 itest(爱测试) 6.6.0 发布 ,新增拖拽生成接口测试断言
    Hyper-V Server + Windows Admin Center
  • 原文地址:https://www.cnblogs.com/zhaokunbokeyuan256/p/7144431.html
Copyright © 2011-2022 走看看