溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Js變量or循環中的var和le的介紹

發布時間:2022-09-13 16:59:00 來源:億速云 閱讀:125 作者:栢白 欄目:開發技術

本篇文章和大家了解一下Js變量or循環中的var和le的介紹。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

目錄
  • 在for循環中使用var聲明初始化帶來的問題

  • 解決方法

    • 使用閉包

    • 使用let變量初始化

  • for循環怎么處理用let和var聲明的初始化變量?

    • 總結

      在for循環中使用var聲明初始化帶來的問題

      // 一道經典面試題:
      var funcs = [];
      for (var i = 0; i < 3; i++) {
        funcs[i] = function() {
          console.log("My value: " + i)
        };
      }
      for (var j = 0; j < 3; j++) {
        funcs[j]();
      }
      /*
      輸出結果:
      > My value: 3
      > My value: 3
      > My value: 3
      */

      會出現這種現象的原因就是:

      • var聲明的作用域是函數作用域而不是塊級作用域,因此在for循環的循環體之外依然能訪問到在初始化for循環時定義的var變量。

      • 且在循環結束后訪問時,訪問到的var變量是已經完成循環后的值。

      解決方法

      使用閉包

      ES5時代的解決辦法就是通過IIFE創建一個閉包,把變量在函數體內保存起來,再執行函數時就不會去訪問外層的var變量了。

      var funcs = [];
      for (var i = 0; i < 3; i++) {
          // 1. 閉包
          funcs[i] = (function (i) {
              return function () {
                  console.log("My value: " + i);
              };
          })(i);
      }
      for (var j = 0; j < 3; j++) {
          funcs[j]();
      }

      使用let變量初始化

      let聲明是塊級作用域,循環體內的變量不會泄露到塊語句之外。

      因此在循環結束后再去訪問變量i時,沒有外層作用域變量的干擾,訪問到的自然就是函數體內保存下來的變量值。

      var funcs = [];
      // 2. let
      for (let i = 0; i < 3; i++) {
        funcs[i] = function() {
          console.log("My value: " + i);
        };
      }
      for (var j = 0; j < 3; j++) {
        funcs[j]();
      }

      從這里也可以看出,使用var來初始化for循環本身就是違反直覺的。

      用來初始化for循環的變量理應是for循環的局部變量,在循環結束以后這個變量就應該沒有意義了才對。

      但是如果使用var來初始化,由于var聲明的變量的作用域是函數作用域,這個初始化變量就和for循環處于同一作用域了,不受for循環的限制。

      本應是for循環的局部變量,卻暴露在了和for循環同層的作用域,且變量值已經被循環次數改變,自然會影響循環結束后其他代碼對該變量的訪問。

      而如果使用let來初始化for循環,就不會有這個困擾了,因為let聲明的作用域是塊級作用域,這個初始化變量會如愿成為for循環的局部變量。

      for循環怎么處理用let和var聲明的初始化變量?

      先上結論:

      • 用var初始化時,for循環會直接使用創建的var初始化變量;

      • 用let初始化時,圓括號會自成一個作用域,for循環會將圓括號內的變量值往循環體內傳遞。

      首先看第一個結論,規范是這么說的: 

      Js變量or循環中的var和le的介紹

      可以看到,規范對于var初始化變量沒有什么特別的處理,直接就拿來用了。 此時這個變量就是個普通的var變量,和for循環處于同一作用域。

      我們用代碼來佐證一下:

      var funcs = [];
      for (var i = 0; i < 3; i++) {
          // ?。?!重復聲明了一個同名的var變量
          var i = 5;
          console.log("My value: " + i);
      }
      /*
      只會輸出一次:
      > My value: 5
      */

      var可以重復聲明且值會覆蓋,因此在循環體內再聲明一個var i = 5,循環變量被作沒了,會直接跳出for循環。

      var funcs = [];
      for (var i = 0; i < 3; i++) {
          // 用let聲明了一個和循環變量同名的變量
          let i = 5;
          console.log("My value: " + i);
      }
      /*
      一共輸出了3次:
      > My value: 5
      > My value: 5
      > My value: 5
      */

      初始化var變量在函數作用域,循環體內的let變量在塊作用域,循環體內優先訪問塊作用域里的let變量,因此循環體內的i值會被覆蓋。

      又由于var變量實際上處于let變量的外層作用域,因此let變量沒有重復聲明,不會報錯;var變量也會如期完成自己作為循環變量的使命。

      再看第二個結論,同樣是先看規范:

      Js變量or循環中的var和le的介紹

      很明顯可以發現,使用let來初始化會比使用var多了一個叫perIterationLets的東西。

      perIterationLets是什么?

      Js變量or循環中的var和le的介紹

      從規范上可以看到,perIterationLets來源于LexicalDeclaration(詞法聲明)里的boundNames。

      而這個LexicalDeclaration(詞法聲明),其實就是我們用來初始化的let聲明。

      可以理解為,如果我們用let聲明來初始化for循環,for循環內部不會像直接使用var變量一樣來直接使用let變量,而是會先把let變量收集起來,以某種形式轉換為perIterationLets,再傳遞給循環體。

      perIterationLets被用來做什么的?

      從規范上可以看到,我們的let變量以perIterationLets的身份,作為參數被傳進了ForBodyEvaluation,也就是循環體里。

      在循環體里,perIterationLets只做了一件事情,那就是作為CreatePerIterationEnvironment的參數:

      Js變量or循環中的var和le的介紹

      從字面上理解,CreatePerIterationEnvironment意思就是每次循環都要創建的環境。

      要注意,這個環境不是{...}里的那些執行語句所處的環境。 {...}里的執行語句是statement,在規范里可以看到,stmt有自己的事情要做。

      這個環境是屬于圓括號的作用域,也就是我們定義的let初始化變量所在的作用域。

      再看看每次循環都要創建的環境被用來干嘛了:

      Js變量or循環中的var和le的介紹

      逐步分析一下方法:CreatePerIterationEnvironment這個

      • 首先,把當前執行上下文的詞法環境保存下來,作為lastIterationEnv(上一次循環時的環境);

      • 創建一個和lastIterationEnv同級的新作用域,作為thisIterationEnv(本次循環的環境);

      • 遍歷我們定義的let初始化變量,也就是perIterationLets,在thisIterationEnv(本次循環的環境)里創建一個同名的可變綁定,找到它們在lastIterationEnv(上一次循環時的環境)里的終值,作為這個同名綁定的初始值;

      • 最后,將thisIterationEnv(本次循環的環境)交還給執行上下文。

      簡而言之就是,for循環會在迭代之前創建一個和初始化變量同名的變量,并使用之前迭代的終值將這個變量初始化以后,再交還給執行上下文。

      用偽代碼理解一下這個過程就是:

      到這里又有一個問題,既然把圓括號內的變量向循環體里傳遞了,那如果在循環體里又重復聲明了一個同名變量,算不算重復聲明,會不會報錯?

      答案是不會。

      因為CreatePerIterationEnvironment在執行時,在新環境里創建的是一個可變的綁定,因此如果在循環體內重復聲明一個名字為i的變量,只是會影響循環體內執行語句對i值的訪問。

      var funcs = [];
      for (let i = 0; i < 3; i++) {
          // ?。?!用let聲明了一個和循環變量同名的變量
          let i = 5;
          console.log("My value: " + i);
      }
      /*
      一共輸出了3次:
      > My value: 5
      > My value: 5
      > My value: 5
      */

      總結

      在for循環中使用var聲明來初始化的話,循環變量會暴露在和for循環同一作用域下,導致循環結束后還能訪問到循環變量,且訪問到的變量值是經過循環迭代后的值。

      解決這個問題的方法如下:

      • 使用閉包將循環變量的值作為局部變量保存起來;

      • 使用ES6的let聲明,將循環變量的作用域限制在for循環內部,初始化變量始終是for循環的局部變量,不能在外界被訪問到。

      for循環是怎么處理用let和var聲明的初始化變量的?

      • 用var初始化時,for循環會直接使用創建的var初始化變量;

      • 用let初始化時,圓括號會自成一個作用域,for循環會將圓括號內的變量值往循環體內傳遞。

      以上就是Js變量or循環中的var和le的介紹的簡略介紹,當然詳細使用上面的不同還得要大家自己使用過才領會。如果想了解更多,歡迎關注億速云行業資訊頻道哦!

      向AI問一下細節

      免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

      AI

      亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女