闭包 closure

目录

闭包是什么

 如何产生闭包

产生闭包的条件

闭包的几种常见形式

1. 函数的嵌套之  函数的返回值是函数 (定义)

2. 函数嵌套之  函数的局部变量是函数 (定义)

 3. 函数嵌套之  全局变量定义为闭包

4. 函数的参数的方式

 5. 循环赋值的方式

6. 迭代器

闭包的作用

1、从外部读取函数内部的变量

2、将创建的变量的值始终保存在内存中

3、封装私有属性和私有方法

闭包面试题

解析

闭包的生命周期

闭包的应用场景

闭包的缺点

缺点

解决办法

补充:内存溢出和内存泄漏

内存溢出

内存泄露

常见的内存泄露:


闭包是什么

《犀牛书》
 函数变量保存在函数作用域内部,这种特性成为闭包
《红宝书》
 闭包是指有权访问另一个 函数作用域中的变量的函数  (函数没导出)
《你不知道的javascript》
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行
 MDN
闭包可以让你在一个内层函数作用域中访问到其外层函数的作用域

当函数的执行导致函数被定义,并抛出(有些书里会定义未抛出,这个并不强制要求)

		
		function foo(){
			var n = 0
		}
		console.log(n) //  Uncaught ReferenceError: n is not defined
		
		function foo1(){
			var m = 0;  //  m 是一个被foo1()创建的局部变量
			function bar1(){// bar1 是内部函数 一个闭包
		     	console.log(m)  // 使用了父函数中声明的变量
			}
			bar1()
		}
		foo1()

在上面的代码中,闭包指的就是bar1()这个函数

 如何产生闭包

  • 当一个嵌套内部子函数引用了嵌套的外部父函数的变量(函数)时,就产生了闭包。注意:是在外部函数执行上下文中创建内部函数时闭包产生的,不是执行时,是创建时

产生闭包的条件

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数),执行函数定义就会产生闭包,不用调用内部函数,但是外部函数要调用。调用外部函数产生新的闭包,调用内部函数不会产生新的闭包
  • 注意:闭包存在于嵌套的内部函数

闭包的几种常见形式

1. 函数的嵌套之  函数的返回值是函数 (定义)

		function foo1(fn){
			var n = 0;
			return function(){
				
			}
		}
		foo1()

2. 函数嵌套之  函数的局部变量是函数 (定义)

		function foo2(){
			var n = function(){
				
			}
			return n
		}
		foo2()()

 3. 函数嵌套之  全局变量定义为闭包

        var outer;
        function foo3(){
            var a = 10
            outer = function(){
                console.log(a)
            }
        }
		foo3() // outer 即闭包被定义
		outer() // 10

4. 函数的参数的方式

        var inner = function(fn){
            console.log(fn())
        }
        
		function foo4(){
			var b = "local";
			var n = function(){ // 此为闭包,为一个返回值为 'local'字符串的函数
				return b
			}
			inner(n);  // 作为inner函数的参数
		};
		foo4()  // 此函数被执行导致闭包 n 产生,并作为inner参数,返回 local

 5. 循环赋值的方式

        function foo5(){
            var arr = [];
            for(var i = 0; i < 10 ; i++){
                arr[i] = (function(j){
                    return function(){
                        console.log(j)
                    }    
                })(i)
            }
            return arr
        }
        var bar = foo5();
        for(var j = 0 ;j < 10; j++){
            bar[j]();
        }

6. 迭代器

        var add =(function(){
            var count = 0;
            return function(){
                return ++count;
            }
        })();
        console.log(add());
        console.log(add())

闭包的作用

变量作用域有两种:全局变量与局部变量;函数内部可以通过作用域链直接读取全局变量;在函数外部无法读取函数内部的变量

能够读取其他函数内部变量的函数,就是闭包

1、从外部读取函数内部的变量

function f1(){
    var n=999;
    function f2(){
      console.log(n); 
    }
    return f2;
  }
  var result=f1();
  result(); // 999

以上代码中,将 f2 作为返回值,就可以在 f1 外部读取它的内部变量。本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

2、将创建的变量的值始终保存在内存中

function f1() {
      var n = 12;
      function f2() {
           console.log(++n);
      };
      return f2;
  }
var result = f1();
result();//13

函数 f1 中的局部变量 n 一直保存在内存当中,并没有在 f1 被调用以后被自动清除

3、封装私有属性和私有方法

function f1(n) {
  return function () {
    return n++;
  };
}
var a1 = f1(1);
a1() // 1
a1() // 2
a1() // 3
var a2 = f1(5);
a2() // 5
a2() // 6
a2() // 7
//这段代码中,a1 和 a2 是相互独立的,各自返回自己的私有变量。

闭包面试题

		function fun(n,o){
			console.log(o);
			return {
				//  返回值是一个对象,对象的key是fun,值为一个函数
				fun:function(m){
					return fun(m,n);
				}
			}
		}
		
		var a = fun(0)  // undefined  
		a.fun(1)  // fun(0).fun(1) 0  
		a.fun(2)  // fun(0).fun(2) 0
		a.fun(3)  // fun(0).fun(3) 0

 		
		var b = fun(0) // undefined
		.fun(1)   // 0   // fun(0).fun(1)
		.fun(2)   // 1   // fun(0).fun(1).fun(2)
		.fun(3)  // 2    // fun(0).fun(1).fun(2).fun(3)
 	 
		
		var c = fun(0).fun(1); 
		c.fun(2); // fun(0).fun(1).fun(2)  // 1
		c.fun(3); // fun(0).fun(1).fun(3)  // 1

解析

对于第一组输出
fun(0)则 n 为 0,o 为 undefined,输出undefined
fun(0)返回一个对象{fun:function(m){return fun(m,n)}},对象的fun属性传入一个参数,即 function调用,且传入参数为 m, 返回 fun(m,n) m 为 1, n 在自己函数作用域中没有定义,向上一级作 用域中寻找,在全局作用域中可以找到 fun 函数的定义,所以 function(m){return fun(m,n)} 是闭 包,是fun函数的调用导致function 函数被定义,且可以访问到外部作用域中的变量 n ,所以其实是执行了fun(1,0),n被赋值为1,o被赋值为 0,输出o为 0 ; 后面不管m传入值变为1,2,3,执行的分别为fun(2,0),fun(3,0), 但是 o 值始终没有改变,所以持续输出 0

 对于第二组输出

fun(0).fun(1) 输出同上为 执行fun(1,0),即fun(n=1,o=0) 的结果输出为 0

fun(0).fun(1).fun(2),由于fun(0).fun(1)实际为fun(1,0)执行,fun函数中的变量 n 被赋值为1,o被赋值为 0,输出 o 为 0,又执行fun(2),传入实参变为2,n为1 ,执行fun(2,1),即fun(n=2,o=1)n 变为 2,o变为1,输出为 1,然后fun又被传参 3,此次执行fun(m=3,n=2),即fun(n=3,o=2)n变为 3 ,o 变为 2, 输出为2 

对于第三组输出

fun(0).fun(1) 输出同上为 执行fun(m=1,n=0) 的结果输出为 0

fun(0).fun(1).fun(2),由于fun(0).fun(1)实际为fun(m=2,n=1),即执行fun(n =2,o=1),fun函数中的变量 n 被赋值为2,o被赋值为 1,输出 o 为1;

fun(0).fun(1).fun(3),执行fun(m=3,n=1),即执行fun(n=3,o=1) 输出为1

闭包的生命周期

  1. 产生:在嵌套内部函数定义执行完了时就产生(不是调用,不是有函数提升嘛)。
  2. 死亡:在嵌套的内部函数成为垃圾对象时。

闭包的应用场景

闭包随处可见,一个 Ajax 请求的成功回调,一个事件绑定的回调函数,一个 setTimeout 的延时回调, 或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影.

1、使用闭包代替全局变量;全局变量有变量污染和变量安全等问题。

//全局变量,test1是全局变量
var test1=111 
function outer(){
alert(test1);
}
outer(); //111
alert(test1); //111

2、函数外或在其他函数中访问某一函数内部的参数;

3、在函数执行之前为要执行的函数提供具体参数;

4、在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数;

5、暂停执行;

6、包装相关功能。

参考:闭包的使用场景有哪些

闭包的缺点

缺点

  • 函数执行完后,函数内部的局部变量没有释放,占有内存时间变长
  • 容易造成内存泄漏

解决办法

  • 能不用闭包就不用
  • 及时释放(就让它等于null就ok,不要忘了!让内部函数成为垃圾对象–>回收闭包)

补充:内存溢出和内存泄漏

内存溢出

  • 一种程序运行出现的错误
  • 当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误

内存泄露

内存泄漏是指由于疏忽或错误造成程序未能释放已经不在使用的内存

内存泄漏并非指内存存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该内存之前就失去了对该内存的控制,

简单理解:无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃

  • 占用的内存没有及时释放
  • 内存泄露积累多了就容易导致内存溢出

常见的内存泄露:

  • 意外的全局变量
  • 没有及时清理的计时器或回调函数
  • 闭包
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值