搜尋
首頁web前端js教程javascript中的閉包中的閉包

javascript中的閉包中的閉包

Jun 16, 2020 am 09:30 AM
javascript

寫在前面


JavaScript 一個近乎神話 對於JavaScript有使用經驗但從未真正理解閉包概念的人來說,理解閉包可以說是某種意義上的重生。閉包並不是需要學習新的文法才能使用的工具。閉包的產生是基於詞法作用域寫程式碼時自然產生的結果。換句話說,你不需要要為了閉包而寫閉包,閉包在我們寫的程式碼中隨處可見。 當你真正了解閉包之後,會發現,哦~,原來我以前所敲的代碼中已經出現了很多閉包了鴨!

一個小 demo


仔細看看下面的例子我們會感到奇怪,明明都是呼叫result(),為什麼結果會不一樣呢?

let count=500 //全局作用域
function foo1() {
  let count = 0;//函数全局作用域
  function foo2() {
    count++;//函数内部作用域
    console.log(count);
    return count;
  }
  return foo2;//返回函数
}
let result = foo1();
result();//结果为1
result();//结果为2

首先foo1()返回的是一個foo2()函數,當我們呼叫result()的時候就會回傳foo2()執行的函數,foo2()裡面有什麼呢? 首先我們看到如下有一個count變數,但是沒有定義.我們根據JavaScript的作用域鏈的定義可知,當函數內部的變數沒有定義的時候,就會採用冒泡的方式,向上一級尋找.上一級沒有接著上一級找,直到最頂層window. 如果都沒有,就會報undefined的錯誤.這裡我們在foo1()中找到了count,於是count 1,第一次輸出的是1,沒有什麼問題.

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

但是第二次我們再執行result()的時候就出現了問題,為什麼會是2呢?按照流程,首先再foo2()函數內部尋找count,沒有然後到外層尋找,找到了count=0,這時候count 1應該是1才對.這裡就涉及到閉包的問題了.

javascript中的閉包中的閉包

首先我們在原來的程式碼中加一個debugger,然後到谷歌瀏覽器右鍵檢查,點擊resources就可以看到右邊有一個Closure,瀏覽器的可視化已經證實了這的確是一個閉包.並且count=1已經存儲在了Closure之中.也就說明count=1沒有被銷毀,等下次在呼叫result()的時候count=2.

#認識作用域


要學習Clusure必須了解JavaScript的作用域相關知識作用域包括:

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

  var count = 100; //全局作用域
  function foo1() {
    var count = 0; //函数全局作用域
    return count; //返回函数
  }
  if (count == 1) {
    //块级作用域
    console.log(count);
  }

上面程式碼簡單可以看出作用域分類,需要注意是,一個函數(function)也是區塊級作用域,簡單來說,一般有 {}都可以算做是一個區塊層級作用域.

作用域鏈


作用域裡面嵌套作用域,就形成了作用域鏈. 外部作用域無法存取內部的作用域,看如下範例

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,但是在巢狀的內部foo2()可以存取外部的函數,這就是作用域產生的特殊效果.

明白了作用域鏈,我們再來看個例子(很有迷惑性,仔細看看哦):

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

  function changeName() {
    var name = 'Jay'; //重新定义name
    showName(); //调用showName()
  }
  changeName();

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

  • 詞法作用域(靜態):js查找是按照程式碼書寫時候的位置來決定的,而不是依照呼叫時候位置

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

javascript中的閉包中的閉包

##透過詞法作用域的的規則我們可以再來分析一下

  1. #呼叫changeName()時,找出這個函數

  2. 定義var name = "Jay"

  3. 呼叫showName()

  4. #在changeName()裡面找出是否有showName()這個方法,發現沒有,向外層查找,找到了

  5. 呼叫console.log(name),在函數內部查找有沒有name,沒有,向外查找,找到了, name="Mike"

  6. 輸出Mike

#閉包


了解了上面的知識之後,終於來到了閉包

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


#1.小"黃"書(你不知道的JavaScript): 當函數可以記住並存取所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行.

2.紅寶書(JavaScript高級程式設計) : 閉包是指有權訪問另一個 函數作用域中的變數的函數

非常抽象的一個概念,我自己的一個理解是:

當一個變數(就像上面的name)既不是該函數內部的局部變數,也不是該函數的參數,相對於作用域來說,就是一個自由變數(引用了外部變數),這樣就會形成一個閉包.
怎麼說呢?我們再來看看一開始我們使用的demo

let count = 500; //全局作用域
function foo1() {
  let count = 0; //函数全局作用域
  function foo2() {
    let count2 = 1; //随便新增一个变量
    // count++;  注释
    debugger;
    //console.log(count); 注释
    //return count;  注释
  }
  return foo2; //返回函数
}
let result = foo1();
result(); //结果为1
result(); //结果为2

再次使用浏览器看看,这时我们就发现Closure已经消失了,这也就证实我说的,如果函数内部不调用外部的变量,就不会形成闭包.但是如果调用了外部变量,那么就会形成闭包. 这也就是说不是所有的函数嵌套函数都能形成闭包

<img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/image/731/425/784/1592270826700856.jpg?x-oss-process=image/resize,p_40" class="lazy" title="1592270826700856.jpg" alt="javascript中的閉包中的閉包">

最后我们再来看一个循环闭包的例子

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

答案 6 6 6 6 6 .因为setTimeout里面的回调函数是一个异步的过程(异步代表可以不用等待我这个代码先执行完,可以先往后执行),而for循环是同步的(代码只能从上往下的执行),立即执行,异步的setTimeout必须等待一秒才能执行,这时i早已经循环结束了.
解决办法有三个:

  1. 将for循环中的var 改成let

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

这样就没有问题了, 因为let是有块级的功能,每一层循环都是独立的,互不影响,所以才能正常输出.
      2. 把setTimeout()套上一个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);
}

这样同样能够实现这个功能,原理和第一个方法一样,每一个log()都是独立的,互不影响,这样才能有正确的结果,var就是因为没有块级的功能,才会出问题 3. 包装成匿名函数

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

前面一个(func..)定义函数,后面一个(i)调用,这再JavaScript叫做立即执行函数,其实与第二种方式是一样的,只是写法不一样.

结语


理解JavaScript闭包是一项重要的技能,在面试中也常常会有,这是迈进高级JavaScript工程师的必经之路.

推荐教程: 《js教程

以上是javascript中的閉包中的閉包的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具