闭包的几种使用场景总结

1、什么是闭包?
闭包实际上是一种函数,闭包就是也是函数技术的一种;闭包能做的事情函数几乎都能做。
闭包也可认为是一种作用域。
闭包技术花式比较多,用法也比较灵活,因为闭包技术的分界线并不明显,几乎无法用一个特点去区分。
闭包产生的时机:内层的作用域访问它外层函数作用域里的参数/变量/函数时,闭包就产生了。
闭包的不足:由于闭包会引用它外层函数作用域里的变量函数,因此,会比其他非闭包形式的函数占用更多内存,当外层函数执行完毕退回函数调用栈(call stack)的时候,外层函数作用域里变量因为被引用着,可能并不会被引擎的垃圾回收器回收,因而会引起内存泄漏。过度使用闭包会导致内存占用过多,甚至内存泄漏。

// 当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包
function test1(){
   var age=19;
}
console.log(age);//Uncaught Reference: age is not defined
function test1(){
   var age=19;
   return function test2(){
        console.log(age)
   }
}
var func=test1();
console.log("func",func);//返回的是test2函数
func();// 19  返回的是test2函数的结果

2、闭包的作用:1、读取函数内部的变量;2、让这些变量始终保持在内存之中。
3、闭包应用场景1–封闭作用域
JavaScript的GC机制
在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象一直会保存在内存中。
封闭作用域又称为封闭空间,小闭包,匿名函数自调
技术最大的目的是:全局变量私有化
优点:1、不污染全局空间;2、内部所有的临时变量执行完毕都会释放不占内存;3、可以保存全局数据;4、更新复杂变量

//基本结构
(function()())();
;(function()())();
+(function()())();
-(function()())();
?(function()())();
//例子
(function (){
    var age=18;
    console.log(age);
})();//匿名函数不需要调用,进入到函数中就执行了,输出为18
(function(){
    console.log(age);
})();//报错了,age is not defined
//虽然var age=18;是一个全局变量,但是在第二个函数中是拿不到的,因为它在一个封闭的作用域中。

3、闭包应用场景2–作用域链
嵌套之间的函数会形成作用域链,每次对变量的访问实际上都是对整条作用域链的遍历查找,先查找最近的作用域,最后再查找全局作用域,如果在某个作用域找到了对量就会结束本次查找过程。
思考?对于作用域全局查找快-?还是局部作用域查找快?
局部作用域查找远远大于全局作用域查找的速度,所以高级程序设计一般尽量避免全局查找。
每次访问都是对作用域链的一次遍历查找,其中全局作用域是最耗时的。
解决方案:当前目标使用完以后,在退出作用域之前储存这个目标,就可以在下次取到上一次的目标。

//作用域链
var num = 3;
(function () {
  (function () {
    (function () {
      (function () {
        (function () {
          var num=2;
          console.log(num);
        })();
      })();
    })();
  })();
})();//2  是从里往外查找

补充:
变量的生命周期 任何一个变量在内存中都是一个引用,这个变量是有自己的生命周期。周期结束意味着被销毁,一个变量在它当前的作用域内被声明那一刻相当于变量出生,整个当前作用域执终止行完毕并退出作用域相当于变量的寿命。
3、闭包应用场景3–保存作用域
保存作用域是一种更高级的闭包技术,如果函数嵌套函数,那么内部的那个函数将形成作用域闭包。好处就是让指令能够绑定一些全局数据去运行。

var A=function(){
    return function(){};
}
<button id="btn">点我</button>
(function(document){
    var btn=document.getElementById("btn");
    var btn1=document.getElementById("btn");
    var btn2=document.getElementById("btn");
    var btn3=document.getElementById("btn");
})(document)//传入了参数,第一次查找时是在全局查找,后面的直接使用,也就是保存了全局的数据,局部直接使用。

优点:1、全局数据隐藏化;2、可以让某个指令运行时绑定一些隐藏的全局数据在身上。
一句话:将数据绑定在指令上运行,让指令不再依赖全局数据。
3、闭包应用场景4–高级排他

//高级排他
/*1、这种排他需要遍历所有的行*/
var allLis = document.getElementsByTagName("li");
for (var i = 0; i < allLis.length; i++) {
  allLis[i].onmouseover = function () {
    for (var j = 0; j < allLis.length; j++) {
      allLis[j].className = "";
    }
    this.className = "current";
  };
}
/*2、提高性能*/
var allLis = document.getElementsByTagName("li");
var lastOne = 0;
for (var i = 0; i < allLis.length; i++) {
  (function (index) {
    allLis[i].onmouseover = function () {
      //清除
      allLis[lastOne].className = "";
      //设置
      this.className = "current";
      //赋值
      lastOne = index;
    };
  })(i);
}//传入每次循环的当前索引i,从而“锁定”点击时应该传入的索引值i

3、闭包应用场景5–参数传递

 //传统方法,使得图片移动
 function test1(x){
     return function test2(y){
         alert(x+y);
     }
 }
 test(9)(10);//19
 var left = 0,
   right = 0,
   speed = 50;
 var lImg = document.getElementById("l");
 var rImg = document.getElementById("r");
 lImg.onmousedown = function () {
   left -= speed;
   this.style.marginLeft = left + "px";
 };
 rImg.onmousedown = function () {
   right += speed;
   this.style.marginLeft = right + "px";
 };
 //闭包(当两个事件同步的时候)
 function move(speed) {
   var num = 0;
   return function () {
     num += speed;
     this.style.marginLeft = num + "px";
   };
 }
 var lImg = document.getElementById("l");
 var rImg = document.getElementById("r");
 lImg.onmousedown = move(-50);
 rImg.onmousedown = move(50);
 //输入的参数是独立的,不会影响。

3、闭包应用场景6–函数节流

//函数节流
/*传统方法*/
var timer = null;
window.onresize = function () {
  clearTimeout(timer);
  timer = setTimeout(function () {
    console.log(document.documentElement.clientWidth);
  }, 400);
};
/*使用闭包实现函数节流*/
function throttle(fn, delay) {
  var timer = null;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(fn, delay);
  };
}
window.onresize = throttle(function () {
  console.log(document.documentElement.clientWidth);
}, 400);

7、延长局部变量的生命
日常开发时,Image对象经常被用于数据统计的上报,示例代码如下:

var report = function (src) {
    var img = new Image();
    img.src = src;
};
report("http://www.xxx.com/getClientInfo"); //把客户端信息上报数据报数据

这段代码在运行时,发现在一些低版本浏览器上存在bug,会丢失部分数据上报。原因是Image对象是report函数中的局部变量,当report函数调用结束后,Image对象随即被JS引擎垃圾回收器回收,而此时可能还没来得及发出http请求,所以可能导致此次上报数据的请求失败。
怎么办呢?我们可以使用闭包把Image对象封闭起来,就可以解决数据丢失的问题,代码如下:

var report = (function() {
    var imgs = [];//在内存里持久化
    return function(src) {
        var img = new Image();
        imgs.push(img);//引用局部变量imgs
        img.src = src;
    }
}());
report('http://www.xxx.com/getClientInfo');//把客户端信息上报数据
  • 12
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值