欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript變量or循環(huán)中的var和let詳解

 更新時(shí)間:2022年09月12日 09:27:49   作者:胖可丁  
這篇文章主要介紹了JavaScript變量or循環(huán)中的var和let詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下

在for循環(huán)中使用var聲明初始化帶來(lái)的問(wèn)題

// 一道經(jīng)典面試題:
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]();
}
/*
輸出結(jié)果:
> My value: 3
> My value: 3
> My value: 3
*/

會(huì)出現(xiàn)這種現(xiàn)象的原因就是:

  • var聲明的作用域是函數(shù)作用域而不是塊級(jí)作用域,因此在for循環(huán)的循環(huán)體之外依然能訪問(wèn)到在初始化for循環(huán)時(shí)定義的var變量。
  • 且在循環(huán)結(jié)束后訪問(wèn)時(shí),訪問(wèn)到的var變量是已經(jīng)完成循環(huán)后的值。

解決方法

使用閉包

ES5時(shí)代的解決辦法就是通過(guò)IIFE創(chuàng)建一個(gè)閉包,把變量在函數(shù)體內(nèi)保存起來(lái),再執(zhí)行函數(shù)時(shí)就不會(huì)去訪問(wèn)外層的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聲明是塊級(jí)作用域,循環(huán)體內(nèi)的變量不會(huì)泄露到塊語(yǔ)句之外。

因此在循環(huán)結(jié)束后再去訪問(wèn)變量i時(shí),沒(méi)有外層作用域變量的干擾,訪問(wèn)到的自然就是函數(shù)體內(nèi)保存下來(lá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來(lái)初始化for循環(huán)本身就是違反直覺(jué)的。

用來(lái)初始化for循環(huán)的變量理應(yīng)是for循環(huán)的局部變量,在循環(huán)結(jié)束以后這個(gè)變量就應(yīng)該沒(méi)有意義了才對(duì)。

但是如果使用var來(lái)初始化,由于var聲明的變量的作用域是函數(shù)作用域,這個(gè)初始化變量就和for循環(huán)處于同一作用域了,不受for循環(huán)的限制。

本應(yīng)是for循環(huán)的局部變量,卻暴露在了和for循環(huán)同層的作用域,且變量值已經(jīng)被循環(huán)次數(shù)改變,自然會(huì)影響循環(huán)結(jié)束后其他代碼對(duì)該變量的訪問(wèn)。

而如果使用let來(lái)初始化for循環(huán),就不會(huì)有這個(gè)困擾了,因?yàn)閘et聲明的作用域是塊級(jí)作用域,這個(gè)初始化變量會(huì)如愿成為for循環(huán)的局部變量。

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

先上結(jié)論:

  • 用var初始化時(shí),for循環(huán)會(huì)直接使用創(chuàng)建的var初始化變量;
  • 用let初始化時(shí),圓括號(hào)會(huì)自成一個(gè)作用域,for循環(huán)會(huì)將圓括號(hào)內(nèi)的變量值往循環(huán)體內(nèi)傳遞。

首先看第一個(gè)結(jié)論,規(guī)范是這么說(shuō)的: 

可以看到,規(guī)范對(duì)于var初始化變量沒(méi)有什么特別的處理,直接就拿來(lái)用了。 此時(shí)這個(gè)變量就是個(gè)普通的var變量,和for循環(huán)處于同一作用域。

我們用代碼來(lái)佐證一下:

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

var可以重復(fù)聲明且值會(huì)覆蓋,因此在循環(huán)體內(nèi)再聲明一個(gè)var i = 5,循環(huán)變量被作沒(méi)了,會(huì)直接跳出for循環(huán)。

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

初始化var變量在函數(shù)作用域,循環(huán)體內(nèi)的let變量在塊作用域,循環(huán)體內(nèi)優(yōu)先訪問(wèn)塊作用域里的let變量,因此循環(huán)體內(nèi)的i值會(huì)被覆蓋。

又由于var變量實(shí)際上處于let變量的外層作用域,因此let變量沒(méi)有重復(fù)聲明,不會(huì)報(bào)錯(cuò);var變量也會(huì)如期完成自己作為循環(huán)變量的使命。

再看第二個(gè)結(jié)論,同樣是先看規(guī)范:

很明顯可以發(fā)現(xiàn),使用let來(lái)初始化會(huì)比使用var多了一個(gè)叫perIterationLets的東西。

perIterationLets是什么?

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

而這個(gè)LexicalDeclaration(詞法聲明),其實(shí)就是我們用來(lái)初始化的let聲明。

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

perIterationLets被用來(lái)做什么的?

從規(guī)范上可以看到,我們的let變量以perIterationLets的身份,作為參數(shù)被傳進(jìn)了ForBodyEvaluation,也就是循環(huán)體里。

在循環(huán)體里,perIterationLets只做了一件事情,那就是作為CreatePerIterationEnvironment的參數(shù):

從字面上理解,CreatePerIterationEnvironment意思就是每次循環(huán)都要?jiǎng)?chuàng)建的環(huán)境。

要注意,這個(gè)環(huán)境不是{...}里的那些執(zhí)行語(yǔ)句所處的環(huán)境。 {...}里的執(zhí)行語(yǔ)句是statement,在規(guī)范里可以看到,stmt有自己的事情要做。

這個(gè)環(huán)境是屬于圓括號(hào)的作用域,也就是我們定義的let初始化變量所在的作用域。

再看看每次循環(huán)都要?jiǎng)?chuàng)建的環(huán)境被用來(lái)干嘛了:

逐步分析一下方法:CreatePerIterationEnvironment這個(gè)

  • 首先,把當(dāng)前執(zhí)行上下文的詞法環(huán)境保存下來(lái),作為lastIterationEnv(上一次循環(huán)時(shí)的環(huán)境);
  • 創(chuàng)建一個(gè)和lastIterationEnv同級(jí)的新作用域,作為thisIterationEnv(本次循環(huán)的環(huán)境);
  • 遍歷我們定義的let初始化變量,也就是perIterationLets,在thisIterationEnv(本次循環(huán)的環(huán)境)里創(chuàng)建一個(gè)同名的可變綁定,找到它們?cè)?code>lastIterationEnv(上一次循環(huán)時(shí)的環(huán)境)里的終值,作為這個(gè)同名綁定的初始值;
  • 最后,將thisIterationEnv(本次循環(huán)的環(huán)境)交還給執(zhí)行上下文。

簡(jiǎn)而言之就是,for循環(huán)會(huì)在迭代之前創(chuàng)建一個(gè)和初始化變量同名的變量,并使用之前迭代的終值將這個(gè)變量初始化以后,再交還給執(zhí)行上下文。

用偽代碼理解一下這個(gè)過(guò)程就是:

到這里又有一個(gè)問(wèn)題,既然把圓括號(hào)內(nèi)的變量向循環(huán)體里傳遞了,那如果在循環(huán)體里又重復(fù)聲明了一個(gè)同名變量,算不算重復(fù)聲明,會(huì)不會(huì)報(bào)錯(cuò)?

答案是不會(huì)。

因?yàn)?code>CreatePerIterationEnvironment在執(zhí)行時(shí),在新環(huán)境里創(chuàng)建的是一個(gè)可變的綁定,因此如果在循環(huán)體內(nèi)重復(fù)聲明一個(gè)名字為i的變量,只是會(huì)影響循環(huán)體內(nèi)執(zhí)行語(yǔ)句對(duì)i值的訪問(wèn)。

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

總結(jié)

在for循環(huán)中使用var聲明來(lái)初始化的話,循環(huán)變量會(huì)暴露在和for循環(huán)同一作用域下,導(dǎo)致循環(huán)結(jié)束后還能訪問(wèn)到循環(huán)變量,且訪問(wèn)到的變量值是經(jīng)過(guò)循環(huán)迭代后的值。

解決這個(gè)問(wèn)題的方法如下:

  • 使用閉包將循環(huán)變量的值作為局部變量保存起來(lái);
  • 使用ES6的let聲明,將循環(huán)變量的作用域限制在for循環(huán)內(nèi)部,初始化變量始終是for循環(huán)的局部變量,不能在外界被訪問(wèn)到。

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

  • 用var初始化時(shí),for循環(huán)會(huì)直接使用創(chuàng)建的var初始化變量;
  • 用let初始化時(shí),圓括號(hào)會(huì)自成一個(gè)作用域,for循環(huán)會(huì)將圓括號(hào)內(nèi)的變量值往循環(huán)體內(nèi)傳遞。

到此這篇關(guān)于JavaScript變量or循環(huán)中的var和let詳解的文章就介紹到這了,更多相關(guān)JS var和let內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS數(shù)組splice操作實(shí)例分析

    JS數(shù)組splice操作實(shí)例分析

    這篇文章主要介紹了JS數(shù)組splice操作,結(jié)合實(shí)例形式分析了javascript使用splice方法刪除數(shù)組元素相關(guān)操作技巧,需要的朋友可以參考下
    2019-10-10
  • ECMAScript6輪播圖實(shí)踐知識(shí)總結(jié)

    ECMAScript6輪播圖實(shí)踐知識(shí)總結(jié)

    最近萌生了用ECMAScript6寫一個(gè)輪播圖的想法,以前就知道ECMAScript6,但是一直沒(méi)有學(xué),現(xiàn)在終于下決心學(xué)了,本篇文章會(huì)總結(jié)在實(shí)踐中用到的ES6的知識(shí)。
    2016-08-08
  • asp.net HttpHandler實(shí)現(xiàn)圖片防盜鏈

    asp.net HttpHandler實(shí)現(xiàn)圖片防盜鏈

    這個(gè)例子來(lái)自于《Maximizing ASP.NET Real World, Object-Oriented Development》一書, 需要的朋友可以參考下。
    2009-11-11
  • 用js+xml自動(dòng)生成表格的東西

    用js+xml自動(dòng)生成表格的東西

    用js+xml自動(dòng)生成表格的東西...
    2006-12-12
  • js 文本滾動(dòng)效果的實(shí)例代碼

    js 文本滾動(dòng)效果的實(shí)例代碼

    一個(gè)簡(jiǎn)單的滾動(dòng)效果,我只測(cè)試了文本,對(duì)于圖片有需要的朋友可以測(cè)試一下哦
    2013-08-08
  • 微信小程序JSON配置文件詳細(xì)講解作用

    微信小程序JSON配置文件詳細(xì)講解作用

    JSON是一種數(shù)據(jù)格式,在實(shí)際開發(fā)中,JSON總是以配置文件的形式出現(xiàn)。小程序項(xiàng)目中也不例外:通過(guò)不同的Json配置文件,可以對(duì)小程序項(xiàng)目進(jìn)行不同級(jí)別的配置
    2022-10-10
  • js獲取一組日期中最近連續(xù)的天數(shù)

    js獲取一組日期中最近連續(xù)的天數(shù)

    這篇文章主要為大家詳細(xì)介紹了js獲取一組日期中最近連續(xù)的天數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • JavaScript中的數(shù)組特性介紹

    JavaScript中的數(shù)組特性介紹

    這篇文章主要介紹了JavaScript中的數(shù)組特性介紹,本文總結(jié)了數(shù)組的3種特性和4種創(chuàng)建數(shù)組的方法,需要的朋友可以參考下
    2014-12-12
  • 用 js 寫一個(gè) js 解釋器過(guò)程詳解

    用 js 寫一個(gè) js 解釋器過(guò)程詳解

    這篇文章主要介紹了用 js 寫一個(gè) js 解釋器過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • Openlayers實(shí)現(xiàn)測(cè)量功能

    Openlayers實(shí)現(xiàn)測(cè)量功能

    這篇文章主要為大家詳細(xì)介紹了Openlayers實(shí)現(xiàn)測(cè)量功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09

最新評(píng)論