js基础知识-闭包

闭包

面试经常问到

  1. 有用过闭包吗?
  2. 什么情况下使用?
  3. 项目中哪些地方使用到闭包?
  4. 有可能造成什么问题?怎么解决?

我在另一篇文章也总结过闭包的一些应用。

变量的作用域

变量的作用域,就是指变量的有效范围。我们最常谈到的是在函数中声明的变量作用域

当在函数中搜索 一个变量的时候,如果该函数内并没有声明这个变量,那么此次搜索的过程会随着代码执行环境 创建的作用域链往外层逐层搜索,一直搜索到全局对象为止。变量的搜索是从内到外。

变量的生命周期

对于全局变量来说,全局变量的生存周期当然是永久的,除非我们主动销毁这个全局变量。 而对于在函数内用 var 关键字声明的局部变量来说,当退出函数时,这些局部变量即失去了 它们的价值,它们都会随着函数调用的结束而被销毁。

闭包

应用场景:

  1. 封装全局变量 计数功能
  2. 延续局部变量的生命周期上报功能
  3. 其实还很多很多,就不一一列举了。
var Type = {};
for ( var i = 0, type; type = [ 'String', 'Array', 'Number' ][ i++ ]; ){
 (function( type ){
 Type[ 'is' + type ] = function( obj ){
 return Object.prototype.toString.call( obj ) === '[object '+ type +']';
 }
 })( type )
};
Type.isArray( [] ); // 输出:true
Type.isString( "str" ); // 输出:true
封装变量
// 1. 未封装
  // 计算参数乘积
  let multi = function() {
    var a = 1
    for(let i = 0, l = arguments.length; i < l; i++) {
      a = a * arguments[i]
    }
    return a
  }
  // 2. 加入缓存机制
  var cache = {}
  let multi = function() {
    let args = Array.prototype.join.call(arguments, ',')
    console.log(args)
    console.log(cache)
    if(cache[args]) {
      return cache[args]
    }
    var a = 1
    for(let i = 0, l = arguments.length; i < l; i++) {
      a = a * arguments[i]
    }
    return cache[args] = a
  }
  // 3. 闭包
  // cache在全局使用,有可能会被无意修改到引发错误。闭包后也可以减少全局变量。
  let multi = (function() {
    let cache = {}
    return function() {
      let args = Array.prototype.join.call(arguments, ',')
      console.log(args)
      console.log(cache)
      if(cache[args]) {
        return cache[args]
      }
      var a = 1
      for(let i = 0, l = arguments.length; i < l; i++) {
        a = a * arguments[i]
      }
      return cache[args] = a
    }
  })()
  // 4. 提炼函数
  let multi = (function() {
    let cache = {}
    let calculate = function() {
      var a = 1
      for(let i = 0, l = arguments.length; i < l; i++) {
        a = a * arguments[i]
      }
      return a
    }
    return function() {
      let args = Array.prototype.join.call(arguments, ',')
      console.log(args)
      console.log(cache)
      if(cache[args]) {
        return cache[args]
      }
      return cache[args] = calculate.apply(null, arguments)
    }
  })()
  
  console.log(multi(1, 2, 3))
  console.log(multi(1, 2, 3))
延续局部变量的寿命

数据上报

var report = function( src ){
 var img = new Image();
 img.src = src;
};
report( 'http://xxx.com/getUserInfo' );

丢失数据的原因是 img 是 report 函数中的局部变量,当 report 函数的 调用结束后,img 局部变量随即被销毁,而此时或许还没来得及发出 HTTP 请求,所以此次请求 就会丢失掉。

闭包和面向对象设计

通常用面向对象思想能实现的功能,用闭包也能实现。反之亦然

// 闭包写法
var extent = function(){
 var value = 0;
 return {
 	call: function(){
 	value++;
 	console.log( value );
 }
     }
};
var extent = extent();
extent.call(); // 输出:1
extent.call(); // 输出:2
extent.call(); // 输出:3

其他写法

// 对象写法
var extent = {
 value: 0,
 call: function(){
 	this.value++;
 	console.log( this.value );
 }
};
extent.call(); // 输出:1
extent.call(); // 输出:2
extent.call(); // 输出:3
// 构造函数写法
var Extent = function(){
 this.value = 0;
};
Extent.prototype.call = function(){
 this.value++;
 console.log( this.value );
};
var extent = new Extent();
extent.call();
extent.call();
extent.call(); 

这里写了三种写法,方便对该实现有个整体的认识。

闭包与内存管理

使用闭包的一部分原因是我们选择主动把一些变量封闭在闭包中,因为可能在以后还需要 使用这些变量,把这些变量放在闭包中和放在全局作用域,对内存方面的影响是一致的,这里并 不能说成是内存泄露

跟闭包和内存泄露有关系的地方是,使用闭包的同时比较容易形成循环引用,如果闭包的作用域链中保存着一些 DOM 节点,这时候就有可能造成内存泄露

在 IE 浏览器中,由于 BOM 和 DOM 中的对象是使用 C++以 COM 对象 的方式实现的,而 COM 对象的垃圾收集机制采用的是引用计数策略。在基于引用计数策略的垃圾回收机制中,如果两个对象之间形成了循环引用,那么这两个对象都无法被回收,但循环引用 造成的内存泄露在本质上也不是闭包造成的

如果要解决循环引用带来的内存泄露问题,我们只需要把循环引用中的变量设为 null 即可。将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运 行时,就会删除这些值并回收它们占用的内存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值