一個有意思的js塊作用域問題

来源:https://www.cnblogs.com/yukixing/archive/2019/09/28/11604888.html
-Advertisement-
Play Games

1.問題 首先把問題放出來,昨天看了一個網友發的一個問題,然後跟我同事一起研究了一下,沒找出來是為什麼,然後我回來一直在想為什麼,然後各種找資料研究,從各個方面找為什麼,比如js上下文,作用域,js垃圾回收,堆棧調用情況等等。 2.js斷點調試找答案 首先如果不看上面的圖,以你現在知道的js知識,你 ...


1.問題

首先把問題放出來,昨天看了一個網友發的一個問題,然後跟我同事一起研究了一下,沒找出來是為什麼,然後我回來一直在想為什麼,然後各種找資料研究,從各個方面找為什麼,比如js上下文,作用域,js垃圾回收,堆棧調用情況等等。

2.js斷點調試找答案

首先如果不看上面的圖,以你現在知道的js知識,你覺得列印出來應該是什麼。第二張圖其實列印出來的結果在意料之中,原因就是函數聲明提升,沒問題,但是第一張圖為什麼呢?這裡可以發散一下思維,比如說是不是在塊作用域中,變數和函數之間存在某種互相覆蓋的問題啊,或者說先在塊中聲明的會被掛載到全局的window對象下麵,後面聲明的就掛載不上去了,並且不會覆蓋,然後可以把代碼稍微改改,驗證一下你的思想,很有意思。然後下麵我們斷點調試看下:

首先進花括弧一步都沒走的時候,但是a和b已經掛載到全局變數的window對象下麵了,這就說明代碼塊中隱式生命的變數是全局變數,代碼相當於這樣:

此時我們再看a在塊作用域中就已經是方法了,註意此時我function a(){}這段代碼還沒走完呢,這就說明函數聲明在js解析(註意是解析不是執行)的時候代碼塊中被提升到了代碼塊頂部,進花括弧的那一刻起,函數就已經被聲明瞭,我們再往下麵一步走

此時a不管在塊作用域還是全局作用域中都變成了a函數,那這裡是都可以理解為運行上面一行代碼,然後就給全局變數下的a賦值為函數呢,我們再看下一步

a=50;走完之後,也就出了塊作用域,此時我們看到沒有了塊作用域,因為已經出了塊作用域了,然後全局對象window裡面a還是函數,並不是50,但是如果你在塊作用域a後面加一行的斷點看的話,此時塊作用域裡面的a的值為50,問題就在這裡,為什麼此時塊作用域裡面的a的值跟全局window對象下麵的值結果不一樣呢?

 

然後我們再往下走一步:

然後進第二個塊作用域,發現跟前面進第一個塊作用域一樣,還沒執行第一行,塊作用域裡面的b已經是函數了,原因也跟第一個一樣js解析的時候函數聲明提升,然後我們再往下走一步:

這一步走完我們發現塊作用域裡面的b已經變成50了,但是全局window對象下麵的b還是undefined,這我也不知道為什麼,那我也就只能說此時b是定義在塊作用域中的內部變數了,再往下走一步

但是當我走出塊作用域的時候,b竟然在全局對象下變成了50,那就證明我上面說的不對,b不是塊作用域中的內部變數,因為此時執行完方法立馬就出塊作用域了,我們看的不是很清楚,我們在方法下麵加一行代碼,方便調試看結果:

確實是當我b函數那一步走完,塊作用域和全局對象window下麵的b都變成了50,那我這裡我就認為是函數b在js解析的時候就被提升到了塊作用域的最上面,執行到b函數那一步其實在之前就已經執行過了,相當於js執行的時候代碼變成下麵這樣:

 

 

 

{
    function b() {};
    b = 50;
}

 

我們再看這個代碼不正是上面a那一個塊作用域的代碼嗎,所以在塊作用域中js執行的時候上下兩個塊作用域中是一樣的,所以在塊作用域中列印a,b得到的結果都是50,然後下一步:

出了塊作用域,就只有全局對象window了,然後window對象下麵的b還是50,所以最後列印出來也是50。走到這一步就所有的步驟都走完了,那麼我們再回頭看上面的a為什麼塊作用域中的值跟window對象下麵的a的值不一樣,通過走完下麵一個代碼塊我們發現上面代碼塊跟下麵代碼塊只有函數放的位置不一樣,結果就不一樣,那我們就看一下這裡函數聲明提升到底是怎麼提升的。

3.塊作用域中的函數聲明提升

然後我就找到阮一峰博客裡面寫的關於es6塊級作用域的文章:

http://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F

另一篇關於js變數的生命周期的文章:

https://dmitripavlutin.com/variables-lifecycle-and-why-let-is-not-hoisted/

在第一篇文章中,看見裡面有真麽一段話:

然後找到這麼一句話,我就點鏈接進去看: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics

發現es6規範裡面真的有對代碼塊作用域的一些規定,但是我也沒太看懂,反正能確定跟這有關係。然後我又點擊了另一個行為方式的鏈接進去看: https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6#comment50817344_31419897

然後在這個回答里找到這麼一個回答,這個回答說代碼塊作用域中定義的函數類似於var定義的變數,(前面阮一峰博客里也是這麼說的),然而第二個綁定僅在塊內部可見,也就是說,第二個綁定在外面是訪問不到的,那用這段話來解釋我們代碼的話就是先不管代碼塊中的函數聲明提升,然後從上面往下運行,看見第一個就綁定到全局的window對象上,第二個就只在函數作用域內可見,那這樣的話我如果在代碼塊內部列印,那結果應該是誰在後面定義我們就列印誰啊,而列印的結果卻是證明瞭函數提升存在的。所以這個好像也解釋不通。然後從這個回答裡面我又找到一個這個鏈接: https://github.com/estools/escope/issues/73

然後的然後我就不知道該怎麼去看這個問題了,但是我相信應該接近答案了,或許答案就藏在上面es6規範B3.3裡面的某個點。當然也可以從調用棧,js垃圾回收,js上下文,js引擎執行解析過程,函數與變數聲明創建原理等等各個方面去分析,這應該是一個值得去分析思考的問題,同時也是很有意思的一個問題,相信你是能學到一些東西的,下麵有一些參考鏈接,如果感興趣可以研究一下,在平常寫代碼的時候可能永遠也不會遇到,很有意思的問題。 參考資料: 1.http://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F 2.http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics 3.https://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6#comment50817344_31419897 4.https://github.com/estools/escope/issues/73 5.https://dmitripavlutin.com/javascript-hoisting-in-details/ 6.https://dmitripavlutin.com/variables-lifecycle-and-why-let-is-not-hoisted/ 7.https://fangyinghang.com/let-in-js/ 8.https://eslint.org/docs/rules/no-inner-declarations 9.https://www.cnblogs.com/liuhe688/p/5891273.html 10.https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/133

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 在我們使用MySQL時,常常會因為不同的原因需要對root用戶密碼進行修改,這篇博客主要介紹了幾種修改root用戶密碼的方式。 未設置root密碼之前: SET PASSWORD命令的方式: mysqladmin命令的方式: UPDATE的方式直接編輯user表: 設置過root之後: mys ...
  • MySQL學習——操作存儲過程 摘要:本文主要學習了使用DDL語句操作存儲過程的方法。 瞭解存儲過程 是什麼 存儲過程是一組為了完成特定功能的SQL語句集合。 使用存儲過程的目的是將常用或複雜的工作預先用SQL語句寫好並用一個指定名稱存儲起來,這個過程經編譯和優化後存儲在資料庫伺服器中,因此稱為存儲 ...
  • 本文基於 Android 9.0 , 代碼倉庫地址 : "android_9.0.0_r45" 文中源碼鏈接: "Zygote.java" "ZygoteInit.java" "ZygoteServer.java" "ZygoteConnection.java" "RuntimeInit.java" ...
  • 最近建了一個 Android 開發交流群,但不限於交流移動端、前端和後端等相關技術。 本群的宗旨:讓所有學習的群友都有進步的機會。 1. 經驗交流 在我們學習時遇到困境,或者開發過程中遇到難題,都可以將你的問題拋出來,也許別人也會遇到過,讓有經驗的人來幫你解答。 2. 文章分享 我會每天分享一篇優質 ...
  • 1. 下載安裝JDK 下載地址: https://www.oracle.com/technetwork/java/javase/downloads/jdk8 downloads 2133151.html 安裝好後,配置 系統環境變數 :控制面板\系統和安全\系統 → 高級系統設置 → 高級 → 環境 ...
  • 因為只有塊元素才會有物理屬性,在css世界裡邊,有三種形態的東西,1. 塊元素。 特性:有物理屬性,width,height寫值起作用,而且要占據一行。2. 內聯元素。 特性:沒有物理屬性。但是margin,padding值有用。不占據一行,後邊可以有兄弟元素。3. 即是塊又是內聯,根據兄弟兄弟元素 ...
  • 在自己的網站上我們如果想把網頁分享出去,只能通過複製鏈接的方式發給好友。這裡將介紹如何使用百度官方提供的分享插件進行分享,在官網裡已經找不到文檔了,但插件還是能用的 ...
  • 還在用 border style: dashed 畫虛線嗎?雖然也是虛線,但是不能控制每一個虛線的寬度 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...