javascript面向对象之闭包

Posted Leo_wlCnBlogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript面向对象之闭包相关的知识,希望对你有一定的参考价值。

javascript面向对象之闭包

学习javascript一段时间了,自己对闭包作出如下总结,如有某点不妥,请君指出,不胜感激!

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

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

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

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

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

闭包案例

复制代码
functon(){
  var num = Math.random;
  return num;  

}
var res = foo();
在这不能直接访问函数内部变量num,只可以间接访问


var r1 = foo();
var r2 = foo();
alert(r1 + \'\\n\'+ r2);
如果使用return返回函数内的数据,理论上不是访问一个数据,
因为在函数运行会分配内存空间,函数调用的时候,数据也会再次创建
复制代码

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

复制代码
function (){
  var num = Math.random();
  return  function (){

   return num;
     }  

}   

var fn = foo();//函数调用一次num就是唯一的
var r1 = fn();
var r2 = fn();
alert(r1 + \'\\n\' +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
复制代码

 

以上是关于javascript面向对象之闭包的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript对象原型链继承闭包

JavaScript对象原型链继承和闭包

JavaScript闭包

第六篇 javascript面向对象

JavaScript碎片———函数闭包(模拟面向对象)

面向对象的JavaScript-009-闭包