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

    写在前面:本文案例除特别指明,默认基于chrome mac版。你可能想:闭包么难道还有浏览器差异么?!嘿嘿,你且看来:

    按我的理解,闭包就是可以在函数外面访问其内部变量的一种方式。因为大家都知道,javascript执行时遵循一个隐式的作用域链,查询变量的值时将沿作用域链一路向上查找。
    先看看常规方法和闭包的不同:

    1. var i='global';
    2. var func=function(){
    3. console.log(i);
    4. }
    5. func();
    此时的输出为'global'。

    这就是常规作用域链的最简单的例子,当我们在执行func()方法时,因为其内部并没有定义i,所以它开始向上查找i值,最终在全局环境中找到i.

    1. var i='global';
    2. function constfuncs(){
    3. var i=10;
    4. var func=function(){
    5. console.log(i);
    6. }
    7. return func;
    8. }
    9. var f=constfuncs();
    10. f();
    此时的输出为10。

    此例中f函数和上例中的func函数一样,在执行时都没有在自身的上下文中找到i值。但不同的是,func函数向上一级查找是到全局环境,而此例中f方法却找到了constfuncs中。啊哈,这就是今天讲的闭包。闭包可以访问到另一个函数的局部变量呢~
    在js中,如果函数执行完,正常情况下,该函数的局部变量将被销毁,外界并不会访问到该函数中的局部变量,比如:

    1. function a(){
    2. var i=10;
    3. console.log(i);//10
    4. }
    5. console.log(i);//undefined

    但是本例中的constfuncs因为返回了一个函数变量,且该函数变量中又引用了constfuncs函数中的i,所以此时constfuncs在执行结束后,其中的变量并不会被销毁,而是要为它返回的函数"留着"。


    且看下面的截图:
    当f函数执行到console.log(i)时,它对应的上下文在调试器中非常清楚:Local指该函数的局部变量,Closure指的就是闭包变量,而Global为全局变量。此时找i值的顺序就是Local || Closure || Global
    如果i值不是一个恒定的值,而是在外层函数执行中进行了一系列的变化,那么当调用返回的函数时,又执行了什么操作呢?

    1. function constfuncs() {
    2. var funcs = [];
    3. for (var i = 0; i < 10; i++) {
    4. funcs[i] = function () {
    5. return i;
    6. }
    7. }
    8. return funcs;
    9. }
    10. var funcs = constfuncs();
    11. alert(funcs[1]());
    此时的输出为10.

    其实仔细来推敲一下,当向funcs数组添加项时,每项都是function(){console.log(i)},这里i值只是简单指向外层的i值。当funcs1执行时,用到i值了,它开始去找闭包变量中的i,而此时i值已经固定,为10。所以,不管执行funcs中的哪一项,它用到i值时,都只能得到最终的i值。
    打断点看清函数在执行时的上下文:Closure中的i值为10。

    那么如何才能使funcs中的每项执行的时候,能用到"被定义时的真实环境"信息呢?也就是,"切断"与初始值i的一些联系,因为i最后又只是一个最终值....看下面 这样行不行:

    1. function constfuncs() {
    2. var funcs = [];
    3. for (var i = 0; i < 10; i++) {
    4. var v = i;
    5. funcs[v] = function () {
    6. return v;
    7. };
    8. }
    9. return funcs;
    10. }
    11. var funcs = constfuncs();
    12. console.log(funcs[1]());

    我没得到一个i值就把它存成v,然后使用v来给funcs赋值,最后我再执行funcs中的函数时,不就不去访问i了嘛?就是访问v了,而每次都新建一个v呢~如果外层函数constfuncs每次都因为闭包的原因把v存起来,我最后不是会有很多个v嘛?我好聪明耶!我都脑洞大开想了一通了,来实践一下:

    结果....结果很让我失望,输出了9.....囧

    为啥呢?
    回头又仔细推敲下我刚才说的,存很多个v??

    人家为啥要给你存很多个 v啊?当constfuncs函数结束执行后,它会把闭包中要用到的变量存起来没错,但是人家不能记着执行过程都给你存着。注意:我之所以做这个实验是因为v和i我认为是不同的,v是每次都新建一个,而i值是重复改写。浏览器说,你再来感受下:

    1. function constfuncs() {
    2. var i=10,j=30;
    3. var func=function(){
    4. return i;
    5. }
    6. return func;
    7. }
    8. var f = constfuncs();
    9. console.log(f());


    这里又展现出了浏览器的不妥协:我不光给你存个最终值,我连你用不到的值也不存。额...你好机智..
    等等,我们看看其他浏览器的处理,safari是这样的:

    firefox和chrome是一样的处理方式,即能少则少:

    说了这么多,我们还没达到目的呢,刚才那个方法还是被i牵着鼻子走呢?再来!

    1. function construct(v) {
    2. return function(){
    3. return v;
    4. }
    5. }
    6. function constfuncs() {
    7. var funcs = [];
    8. for (var i = 0; i < 10; i++) {
    9. funcs[i] = construct(i);
    10. }
    11. return funcs;
    12. }
    13. var funcs = constfuncs();
    14. console.log(funcs[4]());
    哇哈!输出4!

    这里的关键在于,我把i作为参数传给construct函数声称了一个新函数并赋予funcs中的每一项,而construct返回的每个函数都是有其单独的闭包环境的,就是construct构造的闭包环境啦。且看:

    所以,当闭包形成时,所有被用到的局部变量都被保存,参数也是如此。

    总结

    * 闭包,可以说是一个封闭的环境,对外给出的函数就像一个口子,打开闭包外访问内部的一个门。
    * 当闭包会保存外部可能访问到的其内部的一切值,当然包含其外层函数的参数。
    * 根据浏览器的不同,当产生闭包时,值的保留有些会全部保留(如safari),有些会保留用到的值(如chrome 和 firefox) 。

    感谢看到最后~本人不才,还望能助你理解一二。如有不同见解,还请留言。





  • 相关阅读:
    迭代器设计模式
    python中的turtle库绘制图形
    Tesseract-OCR4.0识别中文与训练字库实例
    java图片处理
    Tess4J OCR简单使用教程
    C++ 排列最优解算法思想
    Swing使用JavaFXweb组件
    java专业术语
    java的分数类
    System Rules 更好的测试
  • 原文地址:https://www.cnblogs.com/rubyisaPM/p/4592497.html
Copyright © 2011-2022 走看看