浅谈-闭包

前情纪要:上次的公司例会上的分享会上我准备了一个关于“闭包”的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基础知识点:变量作用域、链式作用域、垃圾回收机制、函数声明、函数表达式、匿名自执行函数等等概念的理解,闭包是一个知识点,很简单但也不是很好理解,希望对大家有帮助吧。

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

原文地址:https://www.cnblogs.com/zhaokunbokeyuan256/p/7144431.html