zoukankan      html  css  js  c++  java
  • (转)JavaScript: 零基础轻松学闭包(1)

    • 闭包是什么?

    初学javascript的人,都会接触到一个东西叫做闭包,听起来感觉很高大上的。网上也有各种五花八门的解释,其实我个人感觉,没必要用太理论化的观念来看待闭包。

    事实上,你每天都在用闭包,只是你不知道罢了。

    比如:

    var cheese = '奶酪';
    
    var test = function(){
        alert(cheese);
    }

    OK,你已经写了一个闭包。

    • 函数也是一个数据类型

    变量 cheese 是在全局作用域中的一个变量,当你创建了一个 test 函数,那么,test 和 cheese 就共享一个全局作用域。

    你要额外明白的一点是,在js中,函数和变量本质上是一个东西。函数也是一个数据类型。

    从上面的定义中也能看出来这一点。你要是不相信的话,我们来看一下咯。

    alert(cheese);
    alert(test);
    
     
     
     

    让我们再来看看 test 和 cheese各是什么类型:

    alert(typeof test);
    
     
     
    alert(typeof cheese);
    
     
     

    看到了吧,只是类型不同而已,他们都是数据类型。

    唯一的不同点就是,函数类型的 test 可以拥有自己内部逻辑,而string类型的 cheese 只能存放一个字面值,这就是区别,仅此而已。

    一目了然了,唯一不同的就是普通变量是字面值一样的存在,而函数需要打个括号才能执行而已。

    你看,我现在打一个括号:

    test();
    
     
     

    打了括号,才会执行函数里面的逻辑。

    • 作用域

    让我们回到闭包,现在将之前的代码做一个小小的变动:

    var cheese = '奶酪';
    var test = function(){
        alert(cheese);
    }
    
    function test2(){
        var cheese = null;
        test();
    }
    
    test2();

    那么,你觉得现在 alert 出来的是 null 还是奶酪呢?

    思考一下。。。

    对的,弹出来的还是奶酪。

     
     

    之前已经说过了,函数 test 和 变量 cheese 同处于一片蓝天下 -- 同一个作用域。

    函数 test 和 变量 cheese 共同享有的作用域叫做全局作用域,就好像地球一样,我们所有的人都享有这个地球,能够在这里呼吸,吃饭,玩耍。

    对test而言,他能访问到的作用域只有它本身的闭包和全局作用域:

     
    Paste_Image.png

    也就是说,正常情况下他访问不到其他闭包里的内容,在 test2 里面定义的变量跟它没有半毛钱关系,所以弹出来的 cheese 依旧是全局作用域里的 cheese。

    函数可以创造自己的作用域。

    我们刚才定义了一个 test 函数,{ } 包裹起来的部分就形成了一个新的作用域,也就是所谓的闭包。

    其实你深刻了解了作用域的原理后,闭包也就理解了。

    就好比地球是一个全局作用域,你自己家的房子是一个函数,你的房子是私人空间,就是一个局部作用域,也就是你自己建了一个闭包!

    你透过窗户可以看见外边的景色,比如院子里的一棵芭蕉树,你于是通过眼镜观察看到了芭蕉树的颜色,高度,枝干的粗细等等。

    这一棵芭蕉树相当于一个全局变量,你在自己的闭包内可以访问到它的数据。

    所以,在这个例子中,test 就是一个房子,在里面可以通过窗户访问到全局作用域中的奶酪 —— 变量 cheese。

    也就是说,cheese 在被 test 访问到的时候,就进入了它的闭包。

    这样解释,你是否觉得好理解一点呢?

    现在你是否可以理解一开始我说,闭包这东西其实我们天天都在用的意思了呢?

    我们给出闭包的第一个注解:

    1. 闭包就是在函数被创建的时候,存在的一个私有作用域,并且能够访问所有的父级作用域。

    回到刚才的例子:

    var cheese = '奶酪';
    var test = function(){
        alert(cheese);
    }
    
    function test2(){
        var cheese = null;
        test();
    }

    在这个例子中,test 和 test2 各自享有一个作用域,对不对?而且他们互相不能访问。比如,我在 test 中定义的一个变量,test2就无法直接访问。

    var test = function(){
        var i = 10;
    }
    
    function test2(){
        alert(i);
    }
    
    test2();

    像这样,一旦执行 test2 函数,编译就不通过,因为在 test2的闭包内,根本找不到变量 i 。它首先会在自己的闭包内寻找 i,找不到的话就去父级作用域里找,这边的父级就是全局作用域,很遗憾,还是没有。这就是所谓的作用域链,它会一级一级往上找。如果找到最顶层,还是找不到的话,就会报错了。

     
     

    在这里,还有一个需要注意的点就是:如果某一个闭包中对全局作用域(或父级作用域)中的变量进行了修改,那么任何引用该变量的闭包都会受到牵连。

    这的确是一个需要注意的地方。

    举个例子

    var cheese = '奶酪';
    
    var test = function(){
        cheese = '奶酪被偷吃了!'
    }
    
    function test2(){
        alert(cheese);
    }
    test();
    test2();

    结果是:

     
    Paste_Image.png

    很有趣,是不是呢?

    当我们在定义一个函数,就产生了一个闭包,如果这个函数里面又有若干的内部函数,就是闭包嵌套着闭包。

    像这样:

    function house(){
        
        var footBall = '足球';
        /* 客厅 */
        function livingRoom(){
            var table = '餐桌';
            var sofa = '沙发';
            alert(footBall);
        }
        
        /* 卧室 */
        function bedRoom(){
            var bed = '大床';
        }
        
        livingRoom();
    }
    
    house(); 

    函数house是一个闭包,里面又定义了两个函数,分别是livingRoom客厅,和bedRoom卧室,它们各自形成一个自己的闭包。对它们而言,父级作用域就是house。

    如果我们希望在客厅里踢足球,在livingRoom函数执行的时候,它会先在自己的闭包中找足球,如果没找到,就去house里面找。一层一层往上找,直至找到了为止。当然,这个例子可能不是很恰当。但起码展示了作用域,闭包之间的联系。

    再说明一下, 闭包就是在函数被创建的时候,存在的一个私有作用域,并且能够访问所有的父级作用域。因此,从理论上讲,任何函数都是一个闭包!



    作者:剽悍一小兔
    链接:https://www.jianshu.com/p/6f5833e261ac
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 相关阅读:
    Python练习题 016:猴子吃桃
    Date() 及其 如何验证用户输入的日期是合法的
    关于事件触发的一个小tips
    KVO监听导航栏
    使用CoreText动态下载更换字体
    UICollectionView 的使用
    echarts散点图搭配时间轴
    使用百度echarts画图表的步骤
    canvas画图中drawImage使用
    html5canvas简单画图
  • 原文地址:https://www.cnblogs.com/dragonwave/p/9273580.html
Copyright © 2011-2022 走看看