Javascript閉包實(shí)例詳解和常見面試題

一.什么是閉包

閉包是指有權(quán)訪問另外一個(gè)函數(shù)作用域中的變量的函數(shù).可以理解為(能夠讀取其他函數(shù)內(nèi)部變量的函數(shù))

閉包的作用: 正常函數(shù)執(zhí)行完畢后,里面聲明的變量被垃圾回收處理掉,但是閉包可以讓作用域里的 變量,在函數(shù)執(zhí)行完之后依舊保持沒有被垃圾回收處理掉

二. 閉包的實(shí)例

// 創(chuàng)建閉包最常見的方式函數(shù)作為返回值
function foo() {
  var name = "kebi";
  return function() {
    console.log(name);
  };
}
var bar = foo();
bar(); //打印kebi    --外部函數(shù)訪問內(nèi)部變量

接下來(lái)通過一個(gè)實(shí)例來(lái)感受一下閉包的作用:
接下來(lái)實(shí)現(xiàn)一個(gè)計(jì)數(shù)器大家肯定會(huì)覺得這不是很簡(jiǎn)單嗎

var count = 0;

function add() {
  count = count + 1;
  console.log(count);
}
add(); //確實(shí)實(shí)現(xiàn)了需求
//但是如果需要第二個(gè)計(jì)數(shù)器呢?
//難道要如下這樣寫嗎?
var count1 = 0;

function add1() {
  count1 = count1 + 1;
  console.log(count1);
}
add1(); //確實(shí)實(shí)現(xiàn)了需
當(dāng)我們需要更多地時(shí)候,這樣明顯是不現(xiàn)實(shí)的,這里我們就需要用到閉包.
function addCount() {
  var conut = 0;
  return function() {
    count = count + 1;
    console.log(count);
  };
}

這里解釋一下上邊的過程: addCount() 執(zhí)行的時(shí)候, 返回一個(gè)函數(shù), 函數(shù)是可以創(chuàng)建自己的作用域的, 但是此時(shí)返回的這個(gè)函數(shù)內(nèi)部需要引用 addCount() 作用域下的變量 count, 因此這個(gè) count 是不能被銷毀的.接下來(lái)需要幾個(gè)計(jì)數(shù)器我們就定義幾個(gè)變量就可以,并且他們都不會(huì)互相影響,每個(gè)函數(shù)作用域中還會(huì)保存 count 變量不被銷毀,進(jìn)行不斷的累加

var fun1 = addCount();
fun1(); //1
fun1(); //2
var fun2 = addCount();
fun2(); //1
fun2(); //2

三.常見面試題

1. for 循環(huán)中打印

for (var i = 0; i < 4; i++) {
  setTimeout(function() {
    console.log(i);
  }, 300);
}
上邊打印出來(lái)的都是 4, 可能部分人會(huì)認(rèn)為打印的是 0,1,2,3

原因:js 執(zhí)行的時(shí)候首先會(huì)先執(zhí)行主線程,異步相關(guān)的會(huì)存到異步隊(duì)列里,當(dāng)主線程執(zhí)行完畢開始執(zhí)行異步隊(duì)列, 主線程執(zhí)行完畢后,此時(shí) i 的值為 4,說(shuō)以在執(zhí)行異步隊(duì)列的時(shí)候,打印出來(lái)的都是 4(這里需要大家對(duì) event loop 有所了解(js 的事件循環(huán)機(jī)制))


如何修改使其正常打印:(使用閉包使其正常打印)

//方法一:
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function(i) {
      return function() {
        console.log(i);
      };
    })(i),
    300
  );
}
// 或者
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function() {
      var temp = i;
      return function() {
        console.log(temp);
      };
    })(),
    300
  );
}
//這個(gè)是通過自執(zhí)行函數(shù)返回一個(gè)函數(shù),然后在調(diào)用返回的函數(shù)去獲取自執(zhí)行函數(shù)內(nèi)部的變量,此為閉包

//方法發(fā)二:
for (var i = 0; i < 4; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, 300);
  })(i);
}
// 大部分都認(rèn)為方法一和方法二都是閉包,我認(rèn)為方法一是閉包,而方法二是通過創(chuàng)建一個(gè)自執(zhí)行函數(shù),使變量存在這個(gè)自執(zhí)行函數(shù)的作用域里

2.真實(shí)的獲取多個(gè)元素并添加點(diǎn)擊事件

var op = document.querySelectorAll("p");
for (var j = 0; j < op.length; j++) {
  op[j].onclick = function() {
    alert(j);
  };
}
//alert出來(lái)的值是一樣的
// 解決辦法一:
for (var j = 0; j < op.length; j++) {
  (function(j) {
    op[j].onclick = function() {
      alert(j);
    };
  })(j);
}
// 解決辦法二:
for (var j = 0; j < op.length; j++) {
  op[j].onclick = (function(j) {
    return function() {
      alert(j);
    };
  })(j);
}
//解決方法三其實(shí)和二類似
for (var j = 0; j < op.length; j++) {
  op[j].onclick = (function() {
    var temp = j;
    return function() {
      alert(j);
    };
  })();
}

//這個(gè)例子和例子一幾乎是一樣的大家可以參考例子一

3.閉包的缺陷:

通過上邊的例子也發(fā)現(xiàn), 閉包會(huì)導(dǎo)致內(nèi)存占用過高,因?yàn)樽兞慷紱]有釋放內(nèi)存


歡迎轉(zhuǎn)載和收藏,喜歡的點(diǎn)個(gè)贊吧?(^_-)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容