目錄
在for迴圈中使用var宣告初始化帶來的問題
解決方法
使用閉包
使用let變數初始化
for迴圈怎麼處理用let和var宣告的初始化變數?
总结
首頁 web前端 js教程 簡單了解JavaScript變數or循環中的var和let

簡單了解JavaScript變數or循環中的var和let

Sep 14, 2022 pm 05:18 PM
javascript

本篇文章為大家帶來了關於javascript的相關知識,其中主要介紹了JavaScript變數or循環中的var和let詳解,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,下面一起來看一下,希望對大家有幫助。

簡單了解JavaScript變數or循環中的var和let

【相關推薦:javascript影片教學web前端

在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迴圈會將圓括號內的變數值往迴圈體內傳遞。

先看第一個結論規格是這麼說的: 

可以看到,規範對於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變數也會如期完成自己作為循環變數的使命。

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

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

perIterationLets是什麼?

從規範上可以看到,perIterationLets源自於LexicalDeclaration(詞法宣告)裡的boundNames

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

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

perIterationLets被用來做什麼的?

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

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

从字面上理解,CreatePerIterationEnvironment意思就是每次循环都要创建的环境

要注意,这个环境不是{...}里的那些执行语句所处的环境。 {...}里的执行语句是statement,在规范里可以看到,stmt有自己的事情要做。

这个环境是属于圆括号的作用域,也就是我们定义的let初始化变量所在的作用域。

再看看每次循环都要创建的环境被用来干嘛了:

逐步分析一下方法: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循环会将圆括号内的变量值往循环体内传递。

【相关推荐:javascript视频教程web前端

以上是簡單了解JavaScript變數or循環中的var和let的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

See all articles