成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

javascript中的閉包是什么

JavaScript 一個(gè)近乎神話 對(duì)于JavaScript有使用經(jīng)驗(yàn)但卻從未真正理解閉包概念的人來說,理解閉包可以說是某種意義上的重生。閉包并不是需要學(xué)習(xí)新的語(yǔ)法才能使用的工具。閉包的產(chǎn)生是基于詞法作用域?qū)懘a時(shí)自然產(chǎn)生的結(jié)果。換句話說,你不需要要為了閉包而寫閉包,閉包在我們寫的代碼中隨處可見。 當(dāng)你真正了解閉包之后,會(huì)發(fā)現(xiàn),哦~,原來我以前所敲的代碼中已經(jīng)出現(xiàn)了很多閉包了鴨!

成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供昌江網(wǎng)站建設(shè)、昌江做網(wǎng)站、昌江網(wǎng)站設(shè)計(jì)、昌江網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、昌江企業(yè)網(wǎng)站模板建站服務(wù),十載昌江做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

一個(gè)小 demo


仔細(xì)看看下面的例子我們會(huì)感到奇怪,明明都是調(diào)用result(),為什么結(jié)果會(huì)不一樣呢?

let count=500 //全局作用域
function foo1() {
  let count = 0;//函數(shù)全局作用域
  function foo2() {
    count++;//函數(shù)內(nèi)部作用域
    console.log(count);
    return count;
  }
  return foo2;//返回函數(shù)
}
let result = foo1();
result();//結(jié)果為1
result();//結(jié)果為2

首先foo1()返回的是一個(gè)foo2()函數(shù),當(dāng)我們調(diào)用result()的時(shí)候就會(huì)返回foo2()執(zhí)行的函數(shù),foo2()里面有什么呢? 首先我們看到如下有一個(gè)count變量,但是沒有定義.我們根據(jù)JavaScript的作用域鏈的定義可知,當(dāng)函數(shù)內(nèi)部的變量沒有定義的時(shí)候,就會(huì)采用冒泡的方式,向上一級(jí)尋找.上一級(jí)沒有接著上一級(jí)找,直到最頂層window. 如果都沒有,就會(huì)報(bào)undefined的錯(cuò)誤.這里我們?cè)趂oo1()中找到了count,于是count+1,第一次輸出的是1,沒有什么問題.

 function foo2() {
    count++;
    console.log(count);
    return count;
  }

但是第二次我們?cè)賵?zhí)行result()的時(shí)候就出現(xiàn)了問題,為什么會(huì)是2呢?按照流程,首先再foo2()函數(shù)內(nèi)部尋找count,沒有然后到外層尋找,找到了count=0,這時(shí)候count+1應(yīng)該為1才對(duì).這里就涉及到閉包的問題了.

javascript中的閉包是什么

首先我們?cè)谠瓉淼拇a中加一個(gè)debugger,然后到谷歌瀏覽器右鍵檢查,點(diǎn)擊resources就可以看到右邊有一個(gè)Closure,瀏覽器的可視化已經(jīng)證實(shí)了這的確是一個(gè)閉包.并且count=1已經(jīng)存儲(chǔ)在了Closure之中.也就說明count=1沒有被銷毀,等下次在調(diào)用result()的時(shí)候count=2.

認(rèn)識(shí)作用域


要學(xué)習(xí)Clusure必須了解JavaScript的作用域相關(guān)知識(shí) 作用域包括:

1.全局作用域
2.函數(shù)作用域
4.塊級(jí)作用域(es6 新出,解決 var 問題, 新增 let, const)

  var count = 100; //全局作用域
  function foo1() {
    var count = 0; //函數(shù)全局作用域
    return count; //返回函數(shù)
  }
  if (count == 1) {
    //塊級(jí)作用域
    console.log(count);
  }

上面代碼簡(jiǎn)單可以看出作用域分類,需要注意是,一個(gè)函數(shù)(function)也是塊級(jí)作用域,簡(jiǎn)單來說,一般有 {}都可以算做是一個(gè)塊級(jí)作用域.

作用域鏈


作用域里面嵌套作用域,就形成了作用域鏈. 外部作用域無法訪問內(nèi)部的作用域,看如下例子

function foo(){
var n=1
function foo2(){
  var m=1
  console.log(n) //1
}
foo2()
}
foo()
console.log(n) //err: n is not defined

上述代碼中在全局中無法訪問內(nèi)部的n,但是在嵌套的內(nèi)部foo2()可以訪問外部的函數(shù),這就是作用域產(chǎn)生的特殊效果.

明白了作用域鏈,我們?cè)賮砜磦€(gè)例子(很有迷惑性,仔細(xì)看看哦):

 var name = 'Mike'; //第一次定義name
  function showName() {
    console.log(name);  //輸出 Mike 還是 Jay ?     
  }

  function changeName() {
    var name = 'Jay'; //重新定義name
    showName(); //調(diào)用showName()
  }
  changeName();

上面的例子你覺得輸出的是什么呢?答案是Mike.在這里我們引出了一個(gè)新的概念,詞法作用域作用域有兩種模型:

  • 詞法作用域(靜態(tài)):js查找是按照代碼書寫時(shí)候的位置來決定的,而不是按照調(diào)用時(shí)候位置

  • 動(dòng)態(tài)作用域:目前還有使用的有Perl,Bash (可以自行了解)

javascript中的閉包是什么

通過詞法作用域的的規(guī)則我們可以再來分析一下

  1. 調(diào)用changeName()時(shí),找到這個(gè)函數(shù)

  2. 定義var name = "Jay"

  3. 調(diào)用showName()

  4. 在changeName()里面查找是否有showName()這個(gè)方法,發(fā)現(xiàn)沒有,向外層查找,找到了

  5. 調(diào)用console.log(name),在函數(shù)內(nèi)部查找有沒有name,沒有,向外查找,找到了,name="Mike"

  6. 輸出Mike

閉包


了解了上面的知識(shí)之后,終于來到了閉包

閉包在兩本書上的官方解釋:

1.小"黃"書(你不知道的JavaScript): 當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行.
2.紅寶書(JavaScript高級(jí)程序設(shè)計(jì)): 閉包是指有權(quán)訪問另一個(gè) 函數(shù)作用域中的變量的函數(shù)

非常抽象的一個(gè)概念,我自己的一個(gè)理解是:
當(dāng)一個(gè)變量(就像上面的name)既不是該函數(shù)內(nèi)部的局部變量,也不是該函數(shù)的參數(shù),相對(duì)于作用域來說,就是一個(gè)自由變量(引用了外部變量),這樣就會(huì)形成一個(gè)閉包.
怎么說呢?我們?cè)賮砜纯匆婚_始我們使用的demo

let count = 500; //全局作用域
function foo1() {
  let count = 0; //函數(shù)全局作用域
  function foo2() {
    let count2 = 1; //隨便新增一個(gè)變量
    // count++;  注釋
    debugger;
    //console.log(count); 注釋
    //return count;  注釋
  }
  return foo2; //返回函數(shù)
}
let result = foo1();
result(); //結(jié)果為1
result(); //結(jié)果為2

再次使用瀏覽器看看,這時(shí)我們就發(fā)現(xiàn)Closure已經(jīng)消失了,這也就證實(shí)我說的,如果函數(shù)內(nèi)部不調(diào)用外部的變量,就不會(huì)形成閉包.但是如果調(diào)用了外部變量,那么就會(huì)形成閉包. 這也就是說不是所有的函數(shù)嵌套函數(shù)都能形成閉包

最后我們?cè)賮砜匆粋€(gè)循環(huán)閉包的例子

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    debugger;
    console.log(i); // 輸出什么?   
  }, 1000);
}

答案 6 6 6 6 6 .因?yàn)閟etTimeout里面的回調(diào)函數(shù)是一個(gè)異步的過程(異步代表可以不用等待我這個(gè)代碼先執(zhí)行完,可以先往后執(zhí)行),而for循環(huán)是同步的(代碼只能從上往下的執(zhí)行),立即執(zhí)行,異步的setTimeout必須等待一秒才能執(zhí)行,這時(shí)i早已經(jīng)循環(huán)結(jié)束了.
解決辦法有三個(gè):

  1. 將for循環(huán)中的var 改成let

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    debugger;
    console.log(i); // 1 2 3 4 5 
  }, 1000);
}

這樣就沒有問題了, 因?yàn)閘et是有塊級(jí)的功能,每一層循環(huán)都是獨(dú)立的,互不影響,所以才能正常輸出.
     2. 把setTimeout()套上一個(gè)function

for (var i = 1; i <= 5; i++) {
  log(i); // 1 2 3 4 5
}
function log(i) {
  setTimeout(function timer() {
    debugger;
    console.log(i);
  }, 1000);
}

這樣同樣能夠?qū)崿F(xiàn)這個(gè)功能,原理和第一個(gè)方法一樣,每一個(gè)log()都是獨(dú)立的,互不影響,這樣才能有正確的結(jié)果,var就是因?yàn)闆]有塊級(jí)的功能,才會(huì)出問題 3. 包裝成匿名函數(shù)

for (var i = 1; i <= 5; i++) {
  (function (i) {
    setTimeout(function timer() {
      debugger;
      console.log(i);
    }, 1000);
  })(i)
}

前面一個(gè)(func..)定義函數(shù),后面一個(gè)(i)調(diào)用,這再JavaScript叫做立即執(zhí)行函數(shù),其實(shí)與第二種方式是一樣的,只是寫法不一樣.

結(jié)語(yǔ)


理解JavaScript閉包是一項(xiàng)重要的技能,在面試中也常常會(huì)有,這是邁進(jìn)高級(jí)JavaScript工程師的必經(jīng)之路.

以上就是javascript中的閉包中的閉包的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注創(chuàng)新互聯(lián)其它相關(guān)文章!

本文標(biāo)題:javascript中的閉包是什么
URL標(biāo)題:http://jinyejixie.com/article40/jjhoeo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航云服務(wù)器、小程序開發(fā)、軟件開發(fā)、標(biāo)簽優(yōu)化、搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司
鹤庆县| 长治县| 通渭县| 广南县| 灌阳县| 巨鹿县| 安丘市| 阆中市| 剑川县| 永昌县| 罗城| 喜德县| 古交市| 弥勒县| 东乡族自治县| 常德市| 莒南县| 鄂托克前旗| 临安市| 澎湖县| 剑川县| 太仓市| 老河口市| 闸北区| 钟山县| 顺义区| 保康县| 屏东县| 阿拉善右旗| 云南省| 海原县| 康平县| 阜城县| 四子王旗| 广宁县| 岑巩县| 高碑店市| 阿鲁科尔沁旗| 广州市| 罗江县| 张家界市|