亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript中閉包的詳解

 更新時間:2017年04月01日 08:51:27   作者:_林鑫  
本文主要介紹了JavaScript中閉包的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧

閉包是什么

在 JavaScript 中,閉包是一個讓人很難弄懂的概念。ECMAScript 中給閉包的定義是:閉包,指的是詞法表示包括不被計算的變量的函數(shù),也就是說,函數(shù)可以使用函數(shù)之外定義的變量。

是不是看完這個定義感覺更加懵逼了?別急,我們來分析一下。

  • 閉包是一個函數(shù)
  • 閉包可以使用在它外面定義的變量
  • 閉包存在定義該變量的作用域中

好像有點清晰了,但是使用在它外面定義的變量是什么意思,我們先來看看變量作用域。

變量作用域

變量可分為全局變量和局部變量。全局變量的作用域就是全局性的,在 js 的任何地方都可以使用全局變量。在函數(shù)中使用 var 關(guān)鍵字聲明變量,這時的變量即是局部變量,它的作用域只在聲明該變量的函數(shù)內(nèi),在函數(shù)外面是訪問不到該變量的。

var func = function(){
  var a = 'linxin';
  console.log(a);     // linxin
}
func();
console.log(a);       // Uncaught ReferenceError: a is not defined

作用域相對比較簡單,我們不多講,來看看跟閉包關(guān)系比較大的變量生存周期。

變量生存周期

全局變量,生命周期是永久的。局部變量,當定義該變量的函數(shù)調(diào)用結(jié)束時,該變量就會被垃圾回收機制回收而銷毀。再次調(diào)用該函數(shù)時又會重新定義了一個新變量。

var func = function(){
  var a = 'linxin';
  console.log(a);
}
func();

a 為局部變量,在 func 調(diào)用完之后,a 就會被銷毀了。

var func = function(){
  var a = 'linxin';
  var func1 = function(){
    a += ' a';
    console.log(a);
  }
  return func1;
}
var func2 = func();
func2();          // linxin a
func2();          // linxin a a
func2();          // linxin a a a

可以看出,在第一次調(diào)用完 func2 之后,func 中的變量 a 變成 'linxin a',而沒有被銷毀。因為此時 func1 形成了一個閉包,導致了 a 的生命周期延續(xù)了。

這下子閉包就比較明朗了。

  • 閉包是一個函數(shù),比如上面的 func1 函數(shù)
  • 閉包使用其他函數(shù)定義的變量,使其不被銷毀。比如上面 func1 調(diào)用了變量 a
  • 閉包存在定義該變量的作用域中,變量 a 存在 func 的作用域中,那么 func1 也必然存在這個作用域中。

現(xiàn)在可以說,滿足這三個條件的就是閉包了。

下面我們通過一個簡單而又經(jīng)典的例子來進一步熟悉閉包。

for (var i = 0; i < 4; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}

我們可能會簡單的以為控制臺會打印出 0 1 2 3,可事實卻打印出了 4 4 4 4,這又是為什么呢?我們發(fā)現(xiàn),setTimeout 函數(shù)時異步的,等到函數(shù)執(zhí)行時,for循環(huán)已經(jīng)結(jié)束了,此時的 i 的值為 4,所以 function() { console.log(i) } 去找變量 i,只能拿到 4。

我們想起上一個例子中,閉包使 a 變量的值被保存起來了,那么這里我們也可以用閉包把 0 1 2 3 保存起來。

for (var i = 0; i < 4; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, 0)
  })(i)
}

當 i=0 時,把 0 作為參數(shù)傳進匿名函數(shù)中,此時 function(i){} 此匿名函數(shù)中的 i 的值為 0,等到 setTimeout 執(zhí)行時順著外層去找 i,這時就能拿到 0。如此循環(huán),就能拿到想要的 0 1 2 3。

內(nèi)存管理

在閉包中調(diào)用局部變量,會導致這個局部變量無法及時被銷毀,相當于全局變量一樣會一直占用著內(nèi)存。如果需要回收這些變量占用的內(nèi)存,可以手動將變量設(shè)置為null。

然而在使用閉包的過程中,比較容易形成 JavaScript 對象和 DOM 對象的循環(huán)引用,就有可能造成內(nèi)存泄露。這是因為瀏覽器的垃圾回收機制中,如果兩個對象之間形成了循環(huán)引用,那么它們都無法被回收。

function func() {
  var test = document.getElementById('test');
  test.onclick = function () {
    console.log('hello world');
  }
}

在上面例子中,func 函數(shù)中用匿名函數(shù)創(chuàng)建了一個閉包。變量 test 是 JavaScript 對象,引用了 id 為 test 的 DOM 對象,DOM 對象的 onclick 屬性又引用了閉包,而閉包又可以調(diào)用 test ,因而形成了循環(huán)引用,導致兩個對象都無法被回收。要解決這個問題,只需要把循環(huán)引用中的變量設(shè)為 null 即可。

function func() {
  var test = document.getElementById('test');
  test.onclick = function () {
    console.log('hello world');
  }
  test = null;
}

如果在 func 函數(shù)中不使用匿名函數(shù)創(chuàng)建閉包,而是通過引用一個外部函數(shù),也不會出現(xiàn)循環(huán)引用的問題。

function func() {
  var test = document.getElementById('test');
  test.onclick = funcTest;
}
function funcTest(){
  console.log('hello world');
}

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

最新評論