js从一段闭包代码说起

151 阅读2分钟

引言

对于 js 程序员而言, 很多时候, 我们经常会被问到闭包。由于变量搜索是从内到外的特性, 最直白的解释就是:变量的值是由定义时候的上下文决定的, 而不是代码执行的时候决定的 。 跟面试官这么解释, 往往会被认为是把闭包与变量的作用域搞混了, 认为你对闭包理解不够。而实际上, 并非如此。

变量的声明周期

与闭包有关的概念就是变量的生命周期

  var function(){
    var a=1;
    return function(){
      a++;
      console.log(a);
    }
  }
  
  var f = func();
  var a=100;
  
  f() // 2
  f() // 3
  f() // 4
  f() // 5

函数调用结束后, a并没有消失, 而是再一个闭包环境中一直存活者, 局部变量的生命周期被延续了。

闭包的更多作用

变量封装

  var cache = {};
  
  var mult = function(){
    var args = Array.prototype.join.call(arguments, ',');
    if(cache[args]) {
      return cache[args];
    }
    
    var a=1;
    for(var i=0; i<arguments.length; i++){
      a = a*arguments[i];
    }
    
    return cache[args] = a;
  }
  

以上的代码中, cache 与 mult 被暴露再全局作用于下, 用闭包进行封装

  
  
  var mult = (function(){
    var cache = {};
    
    return function(){
        var args = Array.prototype.join.call(arguments, ',');
        if(cache[args]) {
          return cache[args];
        }

        var a=1;
        for(var i=0; i<arguments.length; i++){
          a = a*arguments[i];
        }
        return cache[args] = a;
    }
  })();

对于上面的代码还可以进一步封装, 但是本文只是讲解闭包。

延续局部变量的寿命

过程和数据的集合, 是面向对象编程最基本的套路, 对象在方法的执行中过程中被引用, 而闭包则是在方法执行的过程中以上下文的形式包含了内容, 面向对象能实现的, 必要也能实现。

闭包实现

  var extent = fucntion(){
    var value-0;
    return {
      call: function(){
        value++;
        console.log(value);
      }
    }
  }

面向对象实现

  vat extent = {
    value: 0,
    call: function(){
      this.value++;
      console.log(this.value);
    }
  }

闭包与内存

经常有人说,闭包的坏处, 一种说法就是闭包会造成内存泄漏, 所以尽量少用闭包。而这种说法最直接的原因就是对于主动回收闭包的内存毫无意识。

局部如果被封闭在闭包形成的环境中, 那么这个局部变量就会一直存在下去, 这样看, 闭包确实会使一些数据无法及时的被销毁。 而对于内存的影响, 并不能说成事内存泄漏。 如果要回收这些变量, 我们可以手动的把这些变量设置为null, null 意味这切断了变量与它此前引用值之间的连接。 当垃圾收集器下次运行的时候, 就会删除这些值并回收他们所占用的内存。