<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i) }, i * 1000) }
      上面這個內(nèi)容會打印什么?
      看過這題的都會知道答案,每隔一秒打印一個5,打印5次。如果我想將每一輪循環(huán)的i打印出來呢,很簡單,將var替換成let;
      這道題真的是考察閉包嗎?
      為什么要有閉包?
      因為在JavaScript中,沒有辦法在函數(shù)外部訪問到函數(shù)內(nèi)部的變量對象。那么反之,有了閉包,我們可以在函數(shù)以外的任何地方訪問到函數(shù)內(nèi)部的變量對象。
      (注意,我這里用的是變量對象,而不是某個變量,因為它是一個合集,準(zhǔn)確的說,是包含了整個函數(shù)作用域。)
      如何寫閉包?
      常見的閉包方式是:
      function fn1() { var a = 1, b = 2; return function() { return a } } var fn2 = fn1(); fn2(); // 1
      這里fn1執(zhí)行完成后,按理說,內(nèi)部的a、b所在的作用域應(yīng)該會銷毀,但是因為閉包的存在,返回的匿名函數(shù)保留了對當(dāng)前作用域的引用,因此我們可以在fn1執(zhí)行完成之后,依然可以訪問到fn1內(nèi)部的變量a,這就是閉包的使用。
      (注意,這里雖然只是return了a,但是變量b也在內(nèi)存中,也沒有銷毀,因為閉包保存的不是某個變量,而是整個變量對象)
      再來看一些其它閉包例子
      function fn1() { var a = 1; setTimeout(function() { console.log(a) }, 1000 ) } fn1(); // 1
      當(dāng)fn1執(zhí)行完成后,內(nèi)部作用域并沒有銷毀,而是被setTimeout保留下來了,因此這也是閉包!
      var a = 1, b = 2; function () {} ..... var btn = document.getElementById('btn'); btn.addEventListener('click', function() {}, false);
      沒錯,這也是閉包!我用DOM2級方式給btn這個dom節(jié)點添加事件,盡管里面什么變量都沒有引入,但依然保留著外界的變量對象,這也是閉包!
      除了上面這些,還有嗎?當(dāng)然有了,比如每一個帶callback回調(diào)函數(shù)的,都是用了閉包,再比如每一個模塊導(dǎo)出的時候,一定會有閉包來訪問一些內(nèi)部的函數(shù)或者變量,這也是閉包!
      好了,現(xiàn)在我懂了
      那我們再來回看最初提的那個問題,思考一下
      為什么原題中的代碼沒有達(dá)到我們期待的效果?
      我們所期待的是,每一次for循環(huán),我們都能保存一個i的副本,將它保留下來并傳給setTimeout,我們每次循環(huán)都會重新定義這個函數(shù),也就是說第一次循環(huán)和第二次循環(huán)中的setTimeout是不一樣的(也就是說循環(huán)結(jié)束的時候,是有5個函數(shù))。題中的代碼也就等同于下面的代碼:
      for (var i = 0; i < 5; i++) { { setTimeout(function() { console.log(i) }, i * 1000) } } 
      setTimeout本身就是一個閉包,而且大括號提供了一個塊級作用域,所以我們理想情況下很容易做到,但是卻失敗了,原因是什么?并不是閉包的問題,而是我們保存的這個i的副本,出了問題。它們都被封閉在一個共享的全局作用域中,實際上只有一個i,看似有了塊級作用域,但是沒起作用,因為是var聲明的變量不存在塊級作用域,因此循環(huán)結(jié)束的時候,“所有”的i,其實也就是一個i,就是5。
      這道題的解題思路是什么?
      其實就是讓var聲明的變量i保留在塊級作用域內(nèi)。
      那么我們再來看,為什么用let能解決這個問題,很簡單,let聲明的變量有塊級作用域,因此i有了5個副本,并且毫不相關(guān),再配合setTimeout的閉包,我們成功了!
      上面那個方法也等于下面這個
      for (var i = 0; i < 5; i++) { {     let j = i; setTimeout(function() { console.log(j) }, j * 1000) } } 
      還有沒有別的方法了,如果不改變var,如何制造塊級作用域?es5里雖然沒有塊級作用域,但是我們有模擬塊級作用域的方法:函數(shù)作用域!
      for (var i = 0; i < 5; i++) { var a = function(j) { setTimeout(function() { console.log(j) }, j * 1000) }; a(i); a = null; } 
      這里為了避免變量a污染全局,最后將a賦值為null,當(dāng)然了,也可以let a ;
      但是這樣寫又有些繁瑣,因為還要創(chuàng)建一個函數(shù)a,然后再銷毀,那能否不這樣呢?
      IIFE!也就是立即執(zhí)行函數(shù)。
      for (var i = 0; i < 5; i++) { (function(j) { setTimeout(function() { console.log(j) }, j * 1000) })(i) } 
      綜合來看,這道題與其說是考閉包,不如說是考塊級作用域的概念,如果硬要考閉包,不如不給代碼,把需求告訴他,讓他手寫一個,這樣才行吧。
      如果有同學(xué)對作用域的概念不是很清楚,或者不是很深入,可以參考筆者另一篇文章:
       
      對了,這里再補充一點之前提過的,當(dāng)我用let替換var的時候,既然每次循環(huán)都是一個塊級作用域,互相不干擾,那為什么i會一直自動加1呢,它是怎么記得上次循環(huán)是多少呢?
      因為JavaScript引擎內(nèi)部會記住上一輪循環(huán)的值,初始化本輪的變量i時,就在上一輪循環(huán)的基礎(chǔ)上進(jìn)行計算。
       
      以上就是今天分享的內(nèi)容了,感興趣的同學(xué)可以留言一起討論哈!
      文字免費,但碼字不易,記得點贊!
      感興趣可關(guān)注一波,謝謝!
       
       
      posted on 2025-05-29 14:50  言先生  閱讀(9)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 亚洲国产精品久久久天堂麻豆宅男 | 国产自产对白一区| 久久精品色妇熟妇丰满人| 亚洲女同精品中文字幕| a级黑人大硬长爽猛出猛进| 色综合久久精品亚洲国产| 亚洲欧美高清在线精品一区二区| 国产一区二区在线有码| 亚洲最大色综合成人av| 乱人伦人妻系列| 少妇人妻互换不带套| 最新国产精品好看的精品| 日本中文字幕有码在线视频 | 福利无遮挡喷水高潮| 人人人澡人人肉久久精品| 久热久热中文字幕综合激情 | 国产精品香蕉在线观看不卡 | 国产精品免费无遮挡无码永久视频 | 九九热免费精品视频在线| 国产精品无码a∨麻豆| 精品国精品国产自在久国产应用男| 偷窥国产亚洲免费视频| 二区三区亚洲精品国产| 97久久久精品综合88久久| 色老头亚洲成人免费影院| 成人欧美日韩一区二区三区| 亚洲v欧美v日韩v国产v| 日韩精品亚洲专在线电影| 在线a久青草视频在线观看| 九九热精品免费在线视频| 另类专区一区二区三区| 欧美性猛交xxxx乱大交丰满| 亚洲国产码专区在线观看| 国产99视频精品免费视频6| 97色伦97色伦国产| 亚洲中文精品一区二区| 天天做天天爱夜夜爽毛片| 日本丰满人妻xxxxxhd| 一区二区在线欧美日韩中文| 亚洲欧美在线一区中文字幕| 国产精品自拍实拍在线看|