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

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

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Web前端入門(mén)第 66 問(wèn):JavaScript 作用域應(yīng)用場(chǎng)景(閉包)

      什么是作用域?

      就像孫悟空給唐僧畫(huà)個(gè)圈圈一樣,這個(gè)圈圈就可以稱(chēng)之為作用域,這個(gè)比喻可能不太形象。

      作用域和孫悟空的圈圈還是有點(diǎn)區(qū)別,作用域內(nèi)部可以獲得作用域外部的變量,而內(nèi)部的變量無(wú)法逃逸到作用域外面,如果逃逸出去了,那就造成內(nèi)存泄漏了,程序?qū)?huì)出現(xiàn)崩潰!

      全局作用域

      可以理解為就是放在 JS 最外層的那部分內(nèi)容,比如:變量、函數(shù)、對(duì)象等等。凡是定義在最外層的內(nèi)容,都是屬于全局作用域,在全局作用域下的任意函數(shù)都可訪問(wèn)到這部分內(nèi)容。

      var wechat = '前端路引';
      
      (function () {
        function test1 () {
          console.log(wechat);
        }
        test1(); // 輸出 '前端路引'
      })()
      

      以上代碼用到了自執(zhí)行函數(shù) (function () {})(),作用就是為了創(chuàng)建一個(gè)局部作用域,避免變量污染全局作用域,在很多優(yōu)秀的插件中都能看到它的影子。

      上面代碼中的 wechat 變量,就是全局作用域下的變量,test1 函數(shù)定義在全局作用域內(nèi)部,所以對(duì)于 test1 函數(shù)來(lái)說(shuō),全局作用域中的變量它都是可以訪問(wèn)的。

      函數(shù)作用域

      也可稱(chēng)之為 局部作用域,生效范圍在函數(shù)內(nèi)部,在函數(shù)外面無(wú)法訪問(wèn)。

      function test2 () {
        var wechat = '前端路引';
        console.log(wechat);
      }
      test2();
      console.log(wechat); // 報(bào)錯(cuò):wechat is not defined
      

      wechat 變量定義在函數(shù)內(nèi)部,便是函數(shù)作用域,在函數(shù)外面無(wú)法訪問(wèn),這就是局部作用域的特性。

      塊級(jí)作用域

      ES6 新增的玩法,一對(duì)花括號(hào)圈出來(lái)的區(qū)域,就稱(chēng)之為塊級(jí)作用域。需注意 var 聲明的變量是不存在塊級(jí)作用域的,只有 letconst 才存在塊級(jí)作用域。

      {
        var wechat1 = '前端路引';
        let wechat2 = '前端路引';
        const wechat3 = '前端路引';
      }
      console.log(wechat1); // 輸出:前端路引
      console.log(wechat2); // 報(bào)錯(cuò):wechat2 is not defined
      console.log(wechat3); // 報(bào)錯(cuò):wechat3 is not defined
      

      或者是像 if 條件判斷的花括號(hào)一樣也存在塊級(jí)作用域:

      if (true) {
        var wechat1 = '前端路引';
        let wechat2 = '前端路引';
        const wechat3 = '前端路引';
      }
      console.log(wechat1); // 輸出:前端路引
      console.log(wechat2); // 報(bào)錯(cuò):wechat2 is not defined
      console.log(wechat3); // 報(bào)錯(cuò):wechat3 is not defined
      

      當(dāng)然其他 while、for、do 等循環(huán)語(yǔ)句也存在塊級(jí)作用域。

      作用域鏈

      作用域鏈總是從內(nèi)部開(kāi)始,一圈一圈往外部查找,比如:

      let globalVal = '全局';
      function outer() {
        let outerVal = '外部';
        function inner() {
          let innerVal = '內(nèi)部';
          console.log(innerVal);    // '內(nèi)部'(當(dāng)前作用域)
          console.log(outerVal);    // '外部'(外層作用域)
          console.log(globalVal);   // '全局'(全局作用域)
          console.log(wechat); // 報(bào)錯(cuò):ReferenceError: wechat is not defined
        }
        inner();
      }
      outer();
      

      當(dāng)內(nèi)部找不到的時(shí)候,就往外一層查找,外層找不到就在全局作用域找,如果全局作用域也找不到,就會(huì)報(bào)錯(cuò) ReferenceError

      閉包使用

      基于作用域的特性,就有前輩們發(fā)現(xiàn)了 閉包 的用法,閉包這個(gè)東東,用得好呢可以說(shuō)是一把利劍,用得不好那就要反噬主人了。

      閉包 的用處就是搭建函數(shù)內(nèi)部和外部的橋梁,使函數(shù)外部可以訪問(wèn)到函數(shù)內(nèi)部的變量。

      閉包的基本樣子

      function test1 () {
        const wechat = '前端路引';
        function test2 () {
          console.log(wechat);
        }
        return test2;
      }
      test1()(); // 輸出:前端路引
      

      上面代碼中 wechat 定義在函數(shù)內(nèi)部,屬于函數(shù)作用域,test2 也定義在函數(shù)內(nèi)部,使用 test2 訪問(wèn) wechat 變量的這種方法,就稱(chēng)之為 閉包

      為什么需要調(diào)用 test1 需要 ()() ?這個(gè)只是一種簡(jiǎn)寫(xiě),其完整寫(xiě)法應(yīng)該是這樣的:

      const temp = test1(); // 獲得 test1 返回的函數(shù)
      temp(); // 執(zhí)行返回函數(shù)輸出:'前端路引'
      

      解決循環(huán)中的陷阱

      在 ES6 出現(xiàn)之前,var 沒(méi)有塊級(jí)作用域這個(gè)特性,所以循環(huán)語(yǔ)句中常常會(huì)出現(xiàn)一些坑,比如:

      for (var i = 0; i < 3; i++) {
        setTimeout(function () {
          console.log(i); // 輸出:3 3 3
        }, 100)
      }
      

      上面代碼會(huì)輸出三次 3,原因是 var 沒(méi)有塊級(jí)作用域,setTimeout 函數(shù)執(zhí)行時(shí)候,獲得的是 for 循環(huán)之后的 i 值,所以最終輸出都是 3。

      使用 let 優(yōu)化:

      for (let i = 0; i < 3; i++) {
        setTimeout(function () {
          console.log(i); // 輸出:0 1 2
        }, 100)
      }
      

      let 的塊級(jí)作用域可以完美保存每次 i 的值,所以最終輸出是 0 1 2,這也相當(dāng)于一種閉包的用法。

      使用閉包優(yōu)化:

      for (var i = 0; i < 3; i++) {
        (function (j) {
          setTimeout(function () {
            console.log(j); // 輸出:0 1 2
          }, 100)
        })(i)
      }
      

      將 i 以函數(shù)參數(shù)的形式傳入,這樣每次循環(huán)后,函數(shù)內(nèi)部獲得的 j 都是當(dāng)時(shí)的 i 值,所以最終輸出是 0 1 2。

      上面代碼可能難以理解,那么換一種寫(xiě)法看看:

      function temp (j) {
        setTimeout(function () {
          console.log(j); // 輸出:0 1 2
        }, 100)
      }
      for (var i = 0; i < 3; i++) {
        temp(i)
      }
      

      這樣寫(xiě)是否一眼就懂了?

      (function (j) {})(i) 這種寫(xiě)法就相當(dāng)于一個(gè)自執(zhí)行函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù) j,每次執(zhí)行的時(shí)候傳入 i 值而已。

      為什么要一個(gè)小括號(hào)把 function (j) {} 包起來(lái)呢?

      如果直接寫(xiě)成 function (j) {}(i),JS 解析器沒(méi)辦法識(shí)別這是一個(gè)函數(shù)調(diào)用,所以需要用小括號(hào)括起來(lái)。也可以寫(xiě)成 !function (j){}(i) ,也是自執(zhí)行函數(shù)的一種方式。其他的一元運(yùn)算符都可以用來(lái)這么玩,比如:

      +function (j) {}(i)
      -function (j) {}(i)
      ~function (j) {}(i)
      

      個(gè)人覺(jué)得還是小括號(hào)比較容易理解。

      私有變量

      模塊化開(kāi)發(fā)的時(shí)候,可以使用閉包封裝內(nèi)部的私有變量,這樣外部就無(wú)法直接訪問(wèn),以保證私有變量安全,比如:

      const counter = (function() {
        let count = 0; // 私有變量
        return {
          increment: () => count++,
          getCount: () => count,
        };
      })();
      
      counter.increment();
      console.log(counter.getCount()); // 1
      console.log(counter.count);      // undefined(無(wú)法直接訪問(wèn))
      

      函數(shù)柯里化

      閉包的又一種使用形式,柯里化就是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)的函數(shù)。如下:

      function add(a) {
        return function(b) {
          return a + b;
        };
      }
      
      const add5 = add(5); // 返回一個(gè)閉包,記住 a=5
      console.log(add5(3)); // 8
      

      內(nèi)存泄漏

      由于閉包中的變量會(huì)常駐內(nèi)存,如果不及時(shí)釋放閉包,那么就會(huì)造成內(nèi)存泄漏,比如:

      function createHeavyObj() {
        const bigData = new Array(1000000).fill('*'); // 生成一個(gè)大對(duì)象
        return () => bigData; // 閉包引用 bigData
      }
      
      let fn = createHeavyObj();
      // 即使不再需要 bigData,它仍被閉包引用,無(wú)法被回收
      // 解決方法:手動(dòng)解除引用
      fn = null; // 解除閉包對(duì) bigData 的引用
      

      如果沒(méi)有 fn = null 這句代碼,那么 bigData 會(huì)一直存在(直到頁(yè)面刷新或者被垃圾回收機(jī)制回收),如果 createHeavyObj 有多個(gè)地方調(diào)用,那么就可能導(dǎo)致內(nèi)存泄漏。

      寫(xiě)在最后

      JS 的代碼,閉包概念隨處可見(jiàn),在使用時(shí)也需特別小心,不放心的時(shí)候,就將變量釋放 xx = null

      posted @ 2025-06-17 10:13  前端路引  閱讀(693)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲区一区二区激情文学| 久久久久青草线蕉综合超碰| 国产一区精品综亚洲av| 欧美亚洲色综久久精品国产| 免费无码黄网站在线观看| 极品人妻少妇一区二区三区| 又大又粗又爽的少妇免费视频| 真人性囗交视频| 吉木乃县| 女人喷水高潮时的视频网站| 嫩草院一区二区乱码| 国产成人高清精品亚洲| 婷婷丁香五月亚洲中文字幕| 久久99精品中文字幕在| 里番全彩爆乳女教师| 大地资源高清免费观看| 久久亚洲人成网站| 庆阳市| 福利在线视频一区二区| 人妻精品动漫H无码中字| 国产成AV人片在线观看天堂无码| 国产欧美日韩视频一区二区三区 | 人体内射精一区二区三区| 男女无遮挡激情视频| 翘臀少妇被扒开屁股日出水爆乳| 国产精品v片在线观看不卡| 视频一区二区 国产视频| 五月婷婷开心中文字幕| 免费无码成人AV片在线| 亚洲高清国产拍精品熟女| 老司机精品影院一区二区三区| 亚洲大尺度一区二区av| 国产女人18毛片水真多1| 久久99久久99精品免视看国产成人| 改则县| 亚洲av日韩在线资源| 2020年最新国产精品正在播放| 亚洲高潮喷水无码AV电影| 亚洲av成人一区二区| 日韩精品射精管理在线观看| 日本亚洲一区二区精品久久 |