• JavsScript 闭包 (Closure)


    欢迎各位大佬指导!!!

    前言:

    ​ 要理解闭包首先需要了解javascript的变量作用域,JavaScript的变量作用域分为两种:

    1. 全局作用域
    2. 局部作用域

    全局变量的作用域是全局性的,在整个javaScript程序中都在,而在函数内声明的变量作用域,只在函数内起作用。

    eg: 在JavaScript中函数内部可以直接访问全局作用域的变量

    var a = 123; // 定义全局变量 a
    function fn(){
    	alert(a); // 在函数内部访问全局变量a
    }
    fn(); // 调用函数 fn() 弹框结果 123
    

    但是函数内的局部变量,在外面不能访问到。

    function fn(){
    	var a = 123; // 定义局部变量
    }
    alert(a); // a 未定义
    

    注意:在函数内部定义局部变量时,如果没有用 var 声明,那这个变量就是全局变量。即外部就可以访问到这个变量。

    如果想要获取函数内定义的局部变量,那么用闭包就可以获取到函数内部的局部变量。

    闭包的概念

    当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,且外部(父)函数被调用,就产生了闭包,简单的说,JavaScript 允许使用内部函数,即函数的定义和函数的表达式位于另一个函数体内,并且这些函数引用所在外部函数内中声明的局部参数、变量、和声明的其它内部函数,在最外层window作用域被调用时,就形成了闭包。

    闭包的作用:

    • 使函数的内部变量(函数)在函数执行完后,仍然存活在内存中,延长了局部变量的生命周期,变量始终保存在内存中,不会随着函数的结束而自动销毁。
    • 将变量(函数)定义在闭包中使用,避免污染全局变量。
    • 定义JS模块 模块中一般包含大量的变量和函数,如果不使用闭包,将会使这些变量(函数)暴露在全局作用域中,这就很有可能会与用户自己的代码产生冲突

    闭包的隐患

    • 函数执行完成后,函数内部的变量没有被释放,占用内存时间会变长容易造成内存泄露 ,应及时释放,将存在闭包的函数置为null。

    是否使用闭包要考虑这两点:

    隔离和数据保存。如果需要隔离数据并形成命名空间,可以使用匿名自执行函数 ;如果还需要在隔离状态下保存数据,保存之后后面还可以继续使用,那就可以使用闭包,如果及不需要隔离和数据保存,那就使用普通函数,在函数调用完后,数据就释放了。

    来看下面的两端代码

    代码一:

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

    运行结果: The Window

    代码二:

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

    运行结果: My Object

    思考一下,这是为何??

    我们来分析一下,首先来看代码一:

    // 首先在Javascript中 定义全局变量、全局对象、全局函数 都自动属于window对象的成员。也就是说它们都是window对象的属性,可以用window.出来。
    var name = "The Window"; // 这是定义了一个name 的全局变量
    var object = {   // 这是定义了一个object 的全局对象
    	name: "My Obejct", // 局部变量 name
    	getNameFunc: function(){ // 局部函数 getNameFunc 返回的是一个匿名函数
    		return function(){
                // 此处的this 指的是window对象,为什么这样说呢,是因为哪个对象调用this所在的函数,this指的就是哪个对象。
                 // alert(object.getNameFunc() ()) 首先调用了 object.getNameFunc的函数返回了一个匿名函数function(){return this.name} 此时这个匿名函数是全局函属于window对象,所以在调用匿名函数的时候,return this.name 的结果是 The Window
    			return this.name; 									 
    		}
    	}
    };
    
    alert(object.getNameFunc() ());
    

    我们再来分析一下 代码二:

    var name = "The Window"; // 同样定义全局的name变量
    var object = {  // 定义全局的object全局对象
        name: "My Object",  // 定义局部变量
        getNameFunc: function(){  // 定义局部函数 getNameFunc()
            var that = this; // 此时当alert(object.getNameFunc()()) 调用时object调用getNameFunc函数,哪个对象调用this,this指的就是哪个对象,显然this指向的是object对象。然后 var that = this 将 object对象赋值给 that 然后返回匿名函数此时匿名函数中引用了that变量,所有当再次的调用匿名函数是return that.name 就等同于 return object.name 即输入结果为 My Object
            return function(){
                return that.name;
            }
        }
        
    };
    
    alert(object.getNameFunc() ());
    

    OK,我们来看一道经典题

    function fun(n,o){
    	console.log(o);
    	return {
    		fun:function(m){
    			return fun(m,n);
    		}
    	}
    }
    var a = fun(0);a.fun(1);a.fun(2);a.fun(3); //输入结果: undefined 0 0 0
    var b = fun(0).fun(1).fun(2).fun(3); //输入结果: undefined 0 1 2
    
    

    思考一下,OK ,我们来分析一下产生结果的原因。

    var a = fun(0);a.fun(1);a.fun(2);a.fun(3); // 我们来拆分一下这段代码
    
    var a= fun(0); 
    a.fun(1);
    a.fun(2);
    a.fun(3);
    // 当执行第一行代码时,调用最外面的fun(n,o)函数,传入0,所以console.log(o)的结果为undefined,因为只传了一个参数。然后返回一个对象
    // {fun:function(m){return fun(m,n)}}
    // 将这个对象赋值给了变量a 即 a = {fun:function(m){return fun(m,n)}}
    
    // 执行第二行代码时,调用fun:function(m)函数,此时m为1;因为a是第一次的对象传入了0。 所以此时的 n 为0;即 return fun(1,0) 再次调用最外面的fun(n,o)函数,打印o为0;
    
    // 当执行第三行代码时,还是调用fun:function(m)函数,此时m为2;因为a是第一次的对象传入了0。 所以此时的 n 为0;即 return fun(2,0) 再次调用最外面的fun(n,o)函数,打印o为0;
    
    // 当执行第四代码时,同上;打印o为0;
    

    Ok ,第一个分析完毕,我们再来分析一下第二个输入出结果。

    var b = fun(0).fun(1).fun(2).fun(3); // 同样我们把这行代码拆分一下
    
    var b = fun(0)
        .fun(1)
        .fun(2)
        .fun(3);
    
    // 当执行第一行代码时,依然是调用最外面的fun(n,o)函数,并且传入n=0; 即第一次打印的o为undefined,然后返回一个对象
    // {fun:function(m){return fun(m,n)}}
    
    // 当执行第二行代码时,调用fun:function(m)函数,因 .fun(1) 此时m为1;此时 n 为上一次的值 0 ,即 return fun(1,0),再次调用最外面的函数 fun(n,o)
    // 此时n 为 1; 打印 o 为 0 ,然后 return 返回。
    
    // 当执行第三行代码时,调用fun:function(m)函数,因 .fun(2) 此时m为2;此时 n 为第二行执行后的值, n=1; 即 return fun(2,1) ,再次调用最外面的函数 fun(n,o) 此时 n 为 2 , o 为 1 ,即 打印 1,然后返回。
    
    // 当执行第四行代码时,调用fun:function(m)函数,因 .fun(3) 此时m为3;此时 n 为第三行执行后的值, n=2; 即 return fun(3,2) ,再次调用最外面的函数fun(n,o) 此时 n 为 3 , o 为 2,即 打印 2, 然后返回。
    
  • 相关阅读:
    搜索专题: HDU1242 Rescue
    搜索专题: HDU2102 A计划
    搜索 问题 D: 神奇密码锁
    HNUSTOJ-1674 水果消除(搜索或并查集)
    搜索专题:问题 E: 挑战ACM迷宫
    【网络流24题】【洛谷P4013】数字梯形问题【费用流】
    【网络流24题】【洛谷P4013】数字梯形问题【费用流】
    【牛客想开了大赛2 B】n的约数【打表】
    【牛客想开了大赛2 B】n的约数【打表】
    【牛客想开了大赛2 A】平面【数论,数学】
  • 原文地址:https://www.cnblogs.com/1204it-ly/p/13903189.html
Copyright © 2020-2023  润新知