zoukankan      html  css  js  c++  java
  • 学习JavaScript闭包

    最近工作之余匆忙去参加了一个面试,感觉可能是一直在工作,不太注意去总结一些知识点,所以回答的我不是特别满意,有一些专业术语和实际技术不能很快的联系起来,印象最深的就是闭包这个,JavaScript的一个特点,也算是一个难点吧,很容易说不清楚或者是理解有偏差,但是实际工作中,很多应用都需要依靠闭包来实现,因此,在此巩固一下。

    一、变量作用域

    首先,要理解闭包,必须理解JavaScript的变量作用域。

    JavaScript变量的作用域分为两种:全局变量和局部变量。

    什么是全局变量呢?全局变量就是在函数外部声明或者是不使用var命令直接声明的变量,全局变量作为window的一个属性。

    什么是局部变量呢?局部变量就是在函数内部定义的变量。

    1.函数内部可以直接读取全局变量。

    var str = "Hello World!";
    function fuc() { alert(str); }
    fuc(); //Hello World

    2.函数外部无法读取局部变量。

     function fuc() {
            var str = 'Hello World';
        }
    alert(str); // error

    Note:一定要养成使用var命令声明变量的习惯,因为如果在函数内部声明变量(期望声明一个局部变量),但是不适用var命令的话,实际上声明了一个全局变量。

    function fuc() { str = "Hello World"; }
    fuc();
    alert(str); //Hello World

    二、如何在函数外部读取局部变量?

    上面说过,函数外部无法读取局部变量,但是,有些情况下我们需要得到函数内部的局部变量怎么办?

    1.函数内部再定义一个函数:

    function fuc() {
         var str = "Hello World";
    
         function fuc2() {
              alert(str);
          }
    
           return fuc2;
    }
    var result = fuc();
    result(); //Hello World

    这个示例中,fuc2函数被包括在函数fuc内部,这时候fuc函数内部的所有局部变量,对于fuc2都是可见的。但是fuc2内部的局部变量对于fuc则是不可见的。这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

    所以,既然fuc2可以读取fuc中的局部变量,那么只要把fuc2作为返回值,我们就可以在fuc外部读取它的内部变量了。

    三、闭包

    1.概念

    什么是闭包呢?闭包就是能够读取其他函数内部变量的函数。在JavaScript语言中,只有函数内部的子函数才能读取局部变量,因此闭包也可以理解成“定义在一个函数内部的函数”。所以,本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    2.闭包的用途

    (1)、在函数外部读取函数内部的变量

    (2)、让函数内部的变量的值始终保持在内存中

    function f1() {
        var n = 0;
        nAdd = function () { n += 1 }
        function f2() {
            alert(n);
        }
        return f2;
    }
    var result = f1();
    result(); // 0
    nAdd();
    result(); // 1

    此段代码,result就是闭包f2函数。它运行了两次,第一次的值是0,第二次的值是1.这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

    那么为什么会这样呢?原因在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

    这段代码中另一个值得注意的地方,就是"nAdd=function{n+=1;}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的变量进行操作。

    四、使用闭包的注意事项

    1、由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决办法是,在退出函数之前,将不使用的局部变量全部删除。

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

    五、练习题

    练习一:

    var name = "The Window";
    var obj = {
         name: "My Object",
         getNameFunc: function () {
                //this是指obj对象
                return function () {
                //this是指window对象
                return this.name; 
            };
        }
    };
    alert(obj.getNameFunc()());

    练习二:

    var name = "The Window";
    var object = {
        name: "My Object",
        getNameFunc: function () {
            var that = this;
            return function () {
                return that.name;
            };
        }
    };
    alert(object.getNameFunc()());

    这两个练习题有一个比较值得注意的地方,this,一般而言,在JavaScript中,this指向函数执行时的当前对象,但是当没有明确的对象的时候,this就是指向全局对象window。

    5.参考链接

    [1] 阮一峰, 学习Javascript闭包(Closure)

  • 相关阅读:
    hdu 1325 Is It A Tree?(并查集)
    hdu 1010 Tempter of the Bone(深搜+奇偶剪枝)
    hdu 1811 Rank of Tetris(并查集+拓扑排序)
    hdu 4324 Triangle LOVE(拓扑排序)
    使用hibernate和struts2实现分页功能
    struts2生成随机验证码图片
    Spring学习篇:IoC知识整理(一)
    HibernateTools工具通过hbm文件自动生成ddl、pojo等代码
    使用struts2的token机制和cookie来防止表单重复提交
    Spring学习篇:AOP知识整理
  • 原文地址:https://www.cnblogs.com/zouyanzhi/p/5504688.html
Copyright © 2011-2022 走看看