js 中的闭包

206 阅读2分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

什么是闭包

  • 一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包.
  • 闭包可以在一个内层函数中访问到其外层函数的作用域.
  • 闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。
function init() {
    var name = "Mozilla"; // name 是一个被 init 创建的局部变量
    function displayName() { // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    displayName();
}
init();

以上例子里,如果想要displayName函数alert,则必须通过init的函数的执行才可以,只可以通过这一种方式进行调用display函数

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

在这个示例中,我们定义了makeAdder(x)函数,它接受一个参数 x,并返回一个新的函数。返回的函数接受一个参数 y,并返回x+y的值.

add5和 add10都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在 add5的环境中,x为 5。而在 add10中,x则为 10。

闭包里面的环境都是独立且干净的.

闭包的特点

  • 让外部访问函数内部变量成为可能;
  •   局部变量会常驻在内存中;
  •   可以避免使用全局变量,防止全局变量污染;
  •   会造成内存泄漏(有一块内存空间被长期占用,而不被释放) (缺点)

用闭包模拟私有方法

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */
  • 我们只创建了一个词法环境,为三个函数所共享:Counter.increment,Counter.decrement 和Counter.value。
  • 包含两个私有项:privateCounter的变量和名为changeBy的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。这三个公共函数是共享同一个环境的闭包

使用闭包需要注意的点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值