zoukankan      html  css  js  c++  java
  • 关于javascript面向对象之闭包

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

    变量的作用域无非就是两种:全局变量和局部变量。

    Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量,而在函数外部无法读取函数内的局部变量。

    注意点,函数内部声明变量的时候,一定要使用var命令。否则变为全局变量。

    简而言之,闭包就是一个受到保护的变量空间。

    闭包案例

    functon(){
      var num = Math.random;
      return num;  
    
    }
    var res = foo();
    在这不能直接访问函数内部变量num,只可以间接访问
    
    
    var r1 = foo();
    var r2 = foo();
    alert(r1 + '
    '+ r2);
    如果使用return返回函数内的数据,理论上不是访问一个数据,
    因为在函数运行会分配内存空间,函数调用的时候,数据也会再次创建

    若要解决以上问题,获得相同的数据如何操作

    function (){
      var num = Math.random();
      return  function (){
    
       return num;
         }  
    
    }   
    
    var fn = foo();//函数调用一次num就是唯一的
    var r1 = fn();
    var r2 = fn();
    alert(r1 + '
    ' +r2)
    //fn是在一个在函数内定义的函数,那么在执行时候可以访问到上一级作用域中的num ,
     因此在最外面,就可以间接访问唯一的num。此处就是用到了闭包,此次起到保护数据的作用

    函数的本质:Function的实例,也是对象。

    执行一个函数,让函数返回对象

    function Foo(){
     var o = {num: 123};
     return o;
    }
    var obj = Foo();
    alert(obj.num);//123

    在调用函数时,函数内部创建一个对象

    o中存储着对象的引用,return是将o中的数据拷贝一份再返回

    返回的结果被obj接收,此时obj存储的是对象的引用

    执行一个函数,让函数返回一个函数

    function Foo(){
     var o = new Function('alert(123)');
     return o;
    }
    var fn = Foo();
    相当于var fn = new Functon('alert(123)');
    alert(fn);//123


    执行一个函数,让函数返回一个数组

    function func(){
      var m = Math.random();
      var n = Math.random();
      return [
            function () { return m;}
            function () { return n;}
        
          ]
    }
    // var fns = func();
    // var m1 = fns[0](); 
    // var n1 = fns[1]();  
    // alert(m1 +','+ n1) ;  
    
    // var m2 = fns[0](); 
    // var n2 = fns[1]();  
    // alert(m2 +','+ n2) ; 
    
     

    在以上两组数据中输出的结果是相同的,

    数组的优点体现出数据的有序性 多个数据可以进行排序

    但是在复杂数据中,数据的序号就不再有优势

    可以用对象对以上函数改进

    function func(){
      var m = Math.random();
      var n = Math.random();
      return {
         get_M: function (){ return m;},
         get_N: function (){ return n;} 
       };    
    }
    // var obj = func();
    // var m1 =obj.get_M();
    // var n1 = obj.get_N();  
    // alert(m1 +','+ n1) ;   

    闭包案例之调用一函数提供两个方法,对num进行赋值和读取
    第一种写法

    function Foo(){ 
         var obj = new Object(); //这里也可以写成 var obj = {};??
         var num;
         obj.get_num = function(){
          return num;
    };
         obj.set_num = function(v){
         num = v;
    
    };
        return obj;
    
    }  
    var o = Foo();
    console.log(o.get_num());//undefined
    console.log(o.set_num(11));//undefined
    console.log(o.get_num(11));//11

    第二种写法

    function Foo() {
                    var num;
                    return {
                        get_num: function () {
                            return num;
                        },
                        set_num: function ( v ) {
                            num = v;
                        }
                    };
                }        
                var o = Foo();        
                console.log( o.get_num() );    //undefined
                console.log(o.set_num( 11 ));//undefined        
                console.log( o.get_num() );//11


    这有一个对以上函数的变式

    function Foo() {
                        var num;
                    return {
                        _num: num,//赋值操作
                        set_num: function ( v ) {
                            num = v;
                        }
                    };
                }
                //相当于下面函数
                function Foo() {
                    var num;
                    var obj = {};
                    obj._num = num;//上面声明的num和此时的_num变量是不同的
                    obj.set_num = function ( v ) {
                        num = v;
                    };
                    return obj;
                }
                
                var o = Foo();        
                console.log( o._num );  //undefined          
                console.log(o.set_num(11));  //undefined          
                console.log(o._num);  //undefined   取不出数据


    闭包的应用:实现私有数据和缓存数据

    闭包案例之斐波那契数列

    没使用闭包的函数式

    var count = 0;
        var fib = function (n){
            count++;
            if(n< 0)throw new Error('不允许出现负数');
            if(n === 0||n === 1)return 1;
    //        return fib(n-1)+fib(n-2);
            return arguments.callee(n-1) + arguments.callee(n-2);     
        }
    
        console.log(fib(16));
        console.log(count);
        //分别计算第1、2、4、8、16、32项对应的次数为1、3、9、67、3193、7049155

    从以上计算的次数可以看出性能的损耗很严重,那么闭包可以在此解决的问题是已经运算过得数据缓存下来

    var count = 0;
        var fib = (function(){    
            var arr = [];
            return function (n){
                count++;
                if(n < 0)throw new Error('不允许出现负数');
                var res = arr[n];//缓存数据 判断有无数据
                if(res !== undefined){
                    return res;
                }
                else {
                        if(n === 0||n ===1) {
                            res = 1;
                        }
                        else{
                        res = fib(n - 1)+fib(n - 2);
                        }    
                    }
                arr[n] = res;
                return res;
                }
        })();
        console.log(fib(100));
        console.log(count);//199

    第二种写法

    var count = 0;
        var fib = (function(){
            var arr = [];
            return function(n){
                count++;
                return    feibo(arr,n);
            }
        })();
        function feibo(arr,n){
            if(n < 0)throw new Error("不允许出现负数");
            var res = arr[n];
            if(res != undefined){
                return res;
            }else{
                if(n === 0 ||n === 1){
                    res = 1;
                }else{
                    res = fib(n - 1) + fib(n - 2);
                }
            }
            arr[n] = res;
            return res;    
        }
        console.log(fib(100));
        console.log(count);


    从上式可以看出闭包带来的好处;

    拓展:谈到数据缓存也可以不用闭包,下面函数则与闭包无关

    var fib = function ( n ) {
                    var res = fib[ n ];   // 先到函数名中取
                    if ( res !== undefined ) {
                        return res;
                    } else {
                        // 如果是 1 或 0 则将 1 返回给 res
                        // 否则递归结果交给 res;
                        
                        if ( n === 0 || n === 1 ) {
                            res =  1;
                        } else {
                            res = arguments.callee( n - 1 ) + 
                                    arguments.callee( n - 2 );
                        }
                        
                        fib[ n ] = res; 
                        // 将计算的结果放到数组中, 那么下一次再计算的
                        // 时候可以直接拿来用, 就不用重新计算
                        fib.len++;//每次赋值完后                    
                        return res;
                    }
                };        
                fib.len = 0;//给函数添加一个属性        
                console.log(fib(100));
                console.log(fib.len)//101


      本人文笔有限,才疏学浅,文中若有不正之处,万望告知,不胜感激!

  • 相关阅读:
    小进步一点
    首个vue.js项目收尾中……
    elememtui(有关权限的那些事)
    vue.js2.0+elementui ——> 后台管理系统
    console.log篇
    select下拉框不能赋值
    vue中的小踩坑(01)
    vue2.0+element-ui(01简单点的单页面)
    lodash(二)对象+循环遍历+排序
    lodash(一)数组
  • 原文地址:https://www.cnblogs.com/goweb/p/5351278.html
Copyright © 2011-2022 走看看