JavaScript之闭包

闭包

什么是闭包?

闭包是指 有权访问另一个函数作用域中的变量 的函数。

简单的来说 就是 一个函数在执行过程中 返回另一个函数/对象 (引用类型 一般都是函数)

如何形成闭包?

1. 在函数内容部,返回一个引用类型(数组,对象,函数,以函数为主)
2. 返回的引用类型(数组,对象,函数,以函数为主),调用使用函数中(父代作用域)的局部作用域变量
3. 在函数的外部,有变量来引用这个函数
function fn(){
	var a=1;
	return function(){
		return a++;
	}
}
var f1 = fn();
console.log(f1());
console.log(f1());

闭包的特点: 优点同时也是缺点

优点:

  • 减少全局变量的使用
  • 可以从函数外部调用,使用函数内部的数据
  • 函数,执行空间不会被销毁

缺点:

  • 私有变量,存储占用空间(主要的)
  • 容易泄露数据信息,不安全
  • 会占用大量的内存空间,造成内存泄露
1, 函数,执行空间不会被销毁

   优点: 空间中的内容,永远存在

   缺点: 会占用大量的内存空间,造成内存泄露

2, 可以从函数外部调用,使用函数内部的数据

   优点: 调用数据更加方便

   缺点: 容易泄露数据信息,不安全

3, 保护私有变量(减少全局变量的使用)

   优点: 私有变量,不会被销毁

   缺点: 私有变量存储占用空间

闭包的应用

  1. 多元素绑定事件
    匿名函数自调用
    for (var i = 0; i < liList.length; i++) {
        // fn(0)
        (function (i) { // i 形式参数  局部变量
            var li = liList[i];
            // 页面加载时  此处只做事件绑定
            li.onclick = function () {  // 当我点击li的时候  页面已经加载完毕
                // li  函数本身没有变量 li,i ( 跳到全局作用域找 li(最后一个)  i(4) )
                li.style.background = "red";
                console.log(li, i);
            }
        })(i);
    }
  1. 函数防抖/函数节流

image-20200919165632244

函数防抖 => 优化版(借助闭包)

image-20200919170023410

    // 函数防抖  
    document.onclick = (function () {
        var flag = false;
        return function () {
        	// 连点,上次响应还未完成,也是false
            if (flag) {
                return false;
            }
            flag = true;
            setTimeout(function () {
                console.log(111111);
                flag = false;
            }, 1000)
        }
   })()

作用域和作用域链(了解)

(https://blog.csdn.net/qappleh/article/details/80311443)

作用域: 函数在执行过程中的有效区域.

作用域链: 在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问

执行环境(或者说执行空间)(execution context)

每个函数运行时都会产生一个执行环境,而这个执行环境怎么表示呢?js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。 (全局GO对象, 函数AO对象);

全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。

js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈(开辟一个执行空间)中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。

举例说明


      var scope = "global"; 
      function fn1(){
         var a = 1;
         var b = 2;
         return scope; 
      }
      function fn2(){
         return scope;
      }
      fn1();
      fn2();

了解了环境变量,再详细讲讲作用域链。
当某个函数第一次被调用时,就会创建一个执行环境(execution context)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([scope])。然后使用this,arguments(arguments在全局环境中不存在)和其他命名参数的值来初始化函数的活动对象(activation object)。当前执行环境的变量对象始终在作用域链的第0位。

可以看到fn1活动对象里并没有scope变量,于是沿着作用域链(scope chain)向后寻找,结果在全局变量对象里找到了scope,所以就返回全局变量对象里的scope值。

作用域链升级应用之闭包

  function outer(){
      var scope = "outer";
      function inner(){
         return scope;
      }
      return inner;
  }
  var fn = outer();
  fn();

outer()内部返回了一个inner函数,当调用outer时,inner函数的作用域链就已经被初始化了(复制父函数的作用域链,再在前端插入自己的活动对象)

一般来说,当某个环境中的所有代码执行完毕后,该环境被销毁(弹出环境栈),保存在其中的所有变量和函数也随之销毁(全局执行环境变量直到应用程序退出,如网页关闭才会被销毁)
但是像上面那种有内部函数的又有所不同,当outer()函数执行结束,执行环境被销毁,但是其关联的活动对象并没有随之销毁,而是一直存在于内存中,因为该活动对象被其内部函数的作用域链所引用。
具体如下图:
outer执行结束,内部函数开始被调用
outer执行环境等待被回收,outer的作用域链对全局变量对象和outer的活动对象引用都断了

像上面这种内部函数的作用域链仍然保持着对父函数活动对象的引用,就是闭包(closure)

  • 18
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值