• 【JavaScript回顾】闭包


      

    什么是闭包?

      闭包是指有权访问另一个 函数作用域中的变量的函数(也就是说,你这个函数用到的变量另外一个域的就算闭包)

      

     <script>
            function f1() {
                var age = 10;
    
                //这个函数就是闭包
                function f2() {
                    return age;
                }
                return f2;
            }
    
            alert(f1()());
    
        </script>

        在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    闭包与变量 (这里需要注意的)

    function createFunctions() {
                var result = new Array();
                for (var i = 0; i < 10; i++) {
                    result[i] = function () {
                        return i;
                    };
                }
                return result;
            }

     如上代码,如果调用以后所有的值都会是10,而不是我们预想的1-10,这是因为每个函数的作用域链中 都保存着 createFunctions()函数的活动对象,所以它们引用的都是同一个变量 i。当 createFunctions()函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量 对象,所以在每个函数内部 i 的值都是 10。但是,我们可以通过创建另一个匿名函数强制让闭包的行为 符合预期,如下所示。

     <script>
            function createFunctions() {
                var result = new Array();
                for (var i = 0; i < 10; i++) {
                    result[i] = function (num) {
                        return function () {
                            return num;
                        }
                    }(i);//这里直接调用了闭包(function(num)这个函数),
                        //所以result存的是function(){return num;}这个函数
                    //当然,也可以直接调用,那么下面就不用写()了:createFunctions()[8]
                }
                return result;
            }
    
            alert(createFunctions()[8]());
        </script>

      在重写了前面的 createFunctions()函数后,每个函数就会返回各自不同的索引值了。在这个版 本中,我们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋 给数组。这里的匿名函数有一个参数 num,也就是终的函数要返回的值。在调用每个匿名函数时,我 们传入了变量 i。由于函数参数是按值传递的,所以就会将变量 i 的当前值复制给参数 num。而在这个 匿名函数内部,又创建并返回了一个访问 num 的闭包。这样一来,result 数组中的每个函数都有自己 num 变量的一个副本,因此就可以返回各自不同的数值了。

    闭包中的this对象

      我们知道,this 对象是在运行时基于函数的执 行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window①。但有时候 由于编写闭包的方式不同,这一点可能不会那么明显。下面来看一个例子

     <script>
            var name = "Windows";
    
            var obj = {
                name: "closure",
                getName: function () {
                    return function () {
                        return this.name;
                    }
                }
            }
            alert(obj.getName()());
        </script> 

      这个例子该死的正确的答案居然是:Windows, 搞了吧!!! Why?

      

      每个函数在被调用时都会自动取得两个特殊变量:this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的 this 对象保存在一个闭包能够访问 到的变量里,就可以让闭包访问该对象了,如下所示

     <script>
            var name = "Windows";
    
            var obj = {
                name: "closure",
                getName: function () {
                    var that = this;
                    return function () {
                        return that.name;
                    }
                }
            }
            alert(obj.getName()());
        </script>

      在定义匿名函数之前,我们把 this 对象赋值给了一个名叫 that 的变量。而在定义了闭包之后,闭包也可以访问这个变量,因为它是我们 在包含函数中特意声名的一个变量。即使在函数返回之后,that 也仍然引用着 object,所以调用 object.getNameFunc()()就返回了"My Object"。

      来看下三种不同的调用getName的方式的不同结果:

            alert(object.getName());    
            alert((object.getName)());
            alert((object.getName = object.getName)());    

      返回值:1.closure

          2.closure

          3.Windows

      

      第一行代码跟平常一样调用了 object.getName(),返回的是"closure",因为 this.name 就是 object.name。第二行代码在调用这个方法前先给它加上了括号。虽然加上括号之后,就好像只 是在引用一个函数,但 this 的值得到了维持,因为 object.getName 和(object.getName)的定义 是相同的。第三行代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是 函数本身,所以 this 的值不能得到维持,结果就返回了"Windows"。

       当然,你不大可能会像第二行和第三行代码一样调用这个方法。不过,这个例子有助于说明即使是 语法的细微变化,都有可能意外改变 this 的值。

    闭包缺点

      除了以上那么恶心的东西之外...最大的缺点还是暂用内存比较大,如果不是必要的还是慎用。

  • 相关阅读:
    Java数据结构和算法总结-数组、二分查找
    Android开发必知--使用View.setId的正确姿势
    Fiddler基本用法以及如何对手机抓包
    Android学习笔记(十四)方便实用的首选项-PreferenceActivity
    Android学习笔记(十三)SharedPreference必须掌握的基础
    设计模式之单例模式
    关于如何控制一个页面的Ajax读数据只读一次的简单解决办法!
    ASP.NET MVC 表单提交多层子级实体集合数据到控制器中
    使用Bootstrap的popover标签中嵌入插件,并且为插件注册事件实现Ajax与后台交互
    关于.Net使用企业库访问MySql数据库
  • 原文地址:https://www.cnblogs.com/guochenkai/p/4011503.html
Copyright © 2020-2023  润新知