对码当歌,猿生几何?

js闭包记录

什么是闭包

通常说的闭包是当一个函数嵌套另一个函数,外部函数将嵌套函数对象作为返回值返回的时候,我们把这种情况称为闭包。

function func() {var num = 0;            //声明局部变量:numfunction f() {          //嵌套函数,在作用域里console.log(++num)
            }return f();             //调用嵌套函数f,并将f的执行结果返回        }
        func();                     //输出:1

函数func()声明了一个局部变量,并定义了一个函数f(),最后将函数f()的执行结果返回。很容易理解输出结果为1。

function func() {var num = 0;            //声明局部变量:numfunction f() {          //嵌套函数,在作用域里console.log(++num)
            }return f;             //返回嵌套函数f        }var ff = func();                     
        ff();                       //输出:1ff();                       //输出:2ff();                       //输出:3

 现在函数func()仅仅返回函数内嵌套的一个函数对象,而不是直接返回结果。在定义函数作用域外面调用嵌套函数,由于这个函数的作用域链是在函数定义的时候就已经创建的。嵌套的函数f()定义在这个作用域链里,并且变量num又是局部变量,不管在何时何地执行函数f(),这种绑定依然有效。因此,局部变量会一直保存下来。正式因为闭包的这一强大特性,才是其显得非常重要。

 闭包函数在形式上有很多种

在这个例子中,父函数v()体内定义了好几种子函数,这些子函数有的是异步事件的回调函数,会进入浏览器的事件循环池,等主线程工作结束后日后再调用这些回调函数,这些子函数,都导致父函数调用完了,不敢注销自己的作用域,因此这些子函数都是闭包函数。

js并不是为了创造闭包而创造,完全只是因为js允许函数嵌套,js函数嵌套还有个函数作用域链的机制,让父函数不敢注销自己作用域中的数据,才会产生所谓闭包。

也正因为这个闭包的特性,闭包函数可以让父函数的数据一直驻留在内存中保存,从而这也是后来js模块化的基础。

闭包的利与弊

1.闭包可以在函数外部读取函数内部的变量。

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

Counter.value(); // 0Counter.increment();
Counter.increment();
Counter.value(); // 2Counter.decrement();
Counter.value(); / 1

上面这种模式称为模块模式。我们使用立即执行函数 IIFE 将代码私有化但是提供了可访问的接口,通过公共接口来访问函数私有的函数和变量。

2.闭包将内部变量始终保存在内存中。

function type(tag) {  return function (data) {return Object.prototype.toString.call(data).toLowerCase() === '[object ' + tag + ']';
  }
}var isNum = type('number');var isString = type('string');

isNum(1); // trueisString('abc'); // true

利用闭包将内部变量(参数)tag 保存在内存中,来封装自己的类型判断函数。

弊端

1.既然闭包会将内部变量一直保存在内存中,如果在程序中大量使用闭包,势必造成内存的泄漏。

$(document).ready(function() {  var button = document.getElementById('button-1');
  button.onclick = function() {
    console.log('hello');return false;
  };
});

解决办法就是手动解除引用

$(document).ready(function() {  var button = document.getElementById('button-1');
  button.onclick = function() {
    console.log('hello');return false;
  };
  button = null; // 添加这一行代码来手动解除引用});

2.如果你将函数作为对象使用,将闭包作为它的方法,应该特别注意不要随意改动函数的私有属性。