閉包

1、什么是閉包

fn套fn,內部fn可引用外部fn的參數和變量
內部被引用的參數和變量,不會被JS垃圾回收機制收回

2、閉包有什麼好處,哪方面應用

1).希望一個變量長期在內在中
2).避免全局變量污染
  a.模塊化代碼
  b.在循環中找到對應元素的索引
3).私有成員存在

3、閉包注意的地方

IE下可能引發內存洩漏

最簡單例子首先:

function aaa() {
  var a = 1;
}
aaa() //执行完后即会被垃圾回收机制收回*/

fn套fn,內部fn可引用外部fn的參數和變量

function aaa() {
  var a = 5;
  function bbb() {
    alert(a)
  }
  return bbb;
}
var c = aaa(); //这里aaa()已经执行完
c(); //c(),表示bbb函数*/

再一個:a能累加,但必須是全局變量

var a = 1;
function aaa() {
  a++;
  alert(a)
}
aaa(); //2
aaa(); //3
alert(a) //a是全局,所以能调用到,下面需要变成局布变量;

內部被引用的參數和變量,不會被JS垃圾回收機制收回

問題:如何使a在執行過程中有效,比如a++能累加,並且a又是一個局部變量?

//當a是局部變量時,不會累加了
function aaa() {
  var a = 1;
  a++;
  alert(a);
}
aaa(); //2
aaa(); //2 ,调用重新生成,不会累加,下面 既让a是局布变量,又可以累加*/
function aaa() {
  var a = 1; //相对于内部可被找到,又不受外部执行函数影响
  return function () { //函数嵌套函数
    a++;
    alert(a)
  }
}
var b = aaa()
b(); //2
b(); //3  即时局布变量又能累加
alert(a) //a is not defined,说明a是局部变量*/

問題:如何把以上函數聲明改寫為函數表達式?

//常識: 
function aaa() {
  alert(1)
}
aaa()
//該寫為函數表達式:
(function () { //可以把aaa去掉
  alert(1)
}) ();

所以問題的答案是:
a.模塊化代碼

var aaa = (function () { //aaa是函数执行完后的返回值
  var a = 1;
  return function () {
    a++
    alert(a)
  }
}) ()
aaa(); //2  aaa(),执行的即是return 后的函数
aaa(); //3  a在外面调用不到,又能累加,模块化代码;

問題:定義函數的私有方法?

var aaa = (function () {
  var a = 1; //a是局部变量,bbb,ccc是它的私有方法 在aaa函数外面调用不到
  function bbb() {
    a++;
    alert(a)
  }
  function ccc() {
    a++;
    alert(a)
  }
  return {   //特別留意:定義形式
    b: bbb,
    c: ccc
  }
}) ()
aaa.b() //2  調用
aaa.c() //3  調用
alert(a) //not defined
alert(bbb) //not defined
alert(ccc) //not defined

b.在循環中找到對應元素的索引

<ul>
    <li>第0個li</li>
    <li>第1個li</li>
    <li>第2個li</li>
</ul>
var li = document.getElementsByTagName('li')
for (var i = 0; i < li.length; i++) {
  li[i].onclick = function () {
    alert(i) //都是3 ,循环执行结束时i=3,alert还未执行,当去点击时才会执行
  }
}

解決一:循環中i當參數傳進來

for (var i = 0; i < li.length; i++) {
  (function (i) { //1、先写匿名函数,当去执行时把i传进来
    li[i].onclick = function () { //2、内部函数可以调用外部函数传进来的参数;
      alert(i)
    }
  }) (i)
}

解決二:寫return返回

for (var i = 0; i < li.length; i++) {
  li[i].onclick = (function (i) { //1、当点击时,3、执行完时 5、然后把i传进来
    return function () { //4、写return返回值
      alert(i)
    }
  }) (i) //2、先执行一次 5、把i传进去 
}      //7、因循环时,执行完毕后i已经存在内存中的i,调用时不是外面的i,所以能弹出0,1,2

iE下可能引发内存泄漏
問題描述:当一个变量box由dom获取或数组对象获取时,它的属性如onclick去引用一个内部fn,内部函数的对像box是去引用外面的,即会引发内部泄漏,IE下首尾互相引用时
解決:onunload後把對像變為null; 提前在外面引用,之後對象置null

var box = document.getElementById('box')
box.onclick = function () {
  alert(box.id)
}

解決一:

window.onunload = function () {
  box.onclick = null; //onunload后把box变为null即可
}

解决二:提前在外面引用,比如 var id = box.id,里面调用alert(id),之后對象置null;

var id = box.id
box.onclick = function () {
  alert(id)
}
box = null;