摘要:調用堆棧是存放原始數據類型的地方除了函數調用之外。上一節中聲明變量后調用堆棧的粗略表示如下。解釋改變的正確方法是更改內存地址。在聲明時,將在調用堆棧上分配內存地址,該值是在堆上分配的內存地址。
這是專門探索 JavaScript 及其所構建的組件的系列文章的第 21 篇。
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
如果你錯過了前面的章節,可以在這里找到它們:
JavaScript 是如何工作的:引擎,運行時和調用堆棧的概述!
JavaScript 是如何工作的:深入V8引擎&編寫優化代碼的5個技巧!
JavaScript 是如何工作的:內存管理+如何處理4個常見的內存泄漏!
JavaScript 是如何工作的:事件循環和異步編程的崛起+ 5種使用 async/await 更好地編碼方式!
JavaScript 是如何工作的:深入探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!
JavaScript 是如何工作的:與 WebAssembly比較 及其使用場景!
JavaScript 是如何工作的:Web Workers的構建塊+ 5個使用他們的場景!
JavaScript 是如何工作的:Service Worker 的生命周期及使用場景!
JavaScript 是如何工作的:Web 推送通知的機制!
JavaScript 是如何工作的:使用 MutationObserver 跟蹤 DOM 的變化!
JavaScript 是如何工作的:渲染引擎和優化其性能的技巧!
JavaScript 是如何工作的:深入網絡層 + 如何優化性能和安全!
JavaScript 是如何工作的:CSS 和 JS 動畫底層原理及如何優化它們的性能!
JavaScript 是如何工作的:解析、抽象語法樹(AST)+ 提升編譯速度5個技巧!
JavaScript 是如何工作的:深入類和繼承內部原理+Babel和 TypeScript 之間轉換!
JavaScript 是如何工作的:存儲引擎+如何選擇合適的存儲API!
JavaScript 是如何工作的:Shadow DOM 的內部結構+如何編寫獨立的組件!
JavaScript 是如何工作的:WebRTC 和對等網絡的機制!
JavaScript 是如何工作的:編寫自己的 Web 開發框架 + React 及其虛擬 DOM 原理!
JavaScript 是如何工作的:模塊的構建以及對應的打包工具
// 聲明一些變量并初始化它們 var a = 5 let b = "xy" const c = true // 分配新值 a = 6 b = b + "z" c = false // 類型錯誤:不可對常量賦值
作為程序員,聲明變量、初始化變量(或不初始化變量)以及稍后為它們分配新值是我們每天都要做的事情。
但是當這樣做的時候會發生什么呢? JavaScript 如何在內部處理這些基本功能? 更重要的是,作為程序員,理解 JavaScript 的底層細節對我們有什么好處。
下面,我打算介紹以下內容:
JS 原始數據類型的變量聲明和賦值
JavaScript內存模型:調用堆棧和堆
JS 引用類型的變量聲明和賦值
let vs const
JS 原始數據類型的變量聲明和賦值讓我們從一個簡單的例子開始。下面,我們聲明一個名為myNumber的變量,并用值23初始化它。
let myNumber = 23
當執行此代碼時,JS將執行:
為變量(myNumber)創建唯一標識符(identifier)。
在內存中分配一個地址(在運行時分配)。
將值 23 存儲在分配的地址。
雖然我們通俗地說,“myNumber 等于 23”,更專業地說,myNumber 等于保存值 23 的內存地址,這是一個值得理解的重要區別。
如果我們要創建一個名為 newVar 的新變量并把 myNumber 賦值給它。
let newVar = myNumber
因為 myNumber 在技術上實際是等于 “0012CCGWH80”,所以 newVar 也等于 “0012CCGWH80”,這是保存值為23的內存地址。通俗地說就是 newVar 現在的值為 23。
因為 myNumber 等于內存地址 0012CCGWH80,所以將它賦值給 newVar 就等于將0012CCGWH80 賦值給 newVar。
現在,如果我這樣做會發生什么:
myNumber = myNumber + 1
myNumber的值肯定是 24。但是newVar的值是否也為 24 呢?,因為它們指向相同的內存地址?
答案是否定的。由于JS中的原始數據類型是不可變的,當 myNumber + 1 解析為24時,JS 將在內存中分配一個新地址,將24作為其值存儲,myNumber將指向新地址。
這是另一個例子:
let myString = "abc" myString = myString + "d"
雖然一個初級 JS 程序員可能會說,字母d只是簡單在原來存放adbc內存地址上的值,從技術上講,這是錯的。當 abc 與 d 拼接時,因為字符串也是JS中的基本數據類型,不可變的,所以需要分配一個新的內存地址,abcd 存儲在這個新的內存地址中,myString 指向這個新的內存地址。
下一步是了解原始數據類型的內存分配位置。
JavaScript 內存模型:調用堆棧和堆JS 內存模型可以理解為有兩個不同的區域:調用堆棧(call stack)和堆(heap)。
調用堆棧是存放原始數據類型的地方(除了函數調用之外)。上一節中聲明變量后調用堆棧的粗略表示如下。
在上圖中,我抽象出了內存地址以顯示每個變量的值。 但是,不要忘記實際上變量指向內存地址,然后保存一個值。 這將是理解 let vs. const 一節的關鍵。
堆是存儲引用類型的地方。跟調用堆棧主要的區別在于,堆可以存儲無序的數據,這些數據可以動態地增長,非常適合數組和對象。
JS 引用類型的變量聲明和賦值讓我們從一個簡單的例子開始。下面,我們聲明一個名為myArray的變量,并用一個空數組初始化它。
let myArray = []
當你聲明變量“myArray”并為其指定非原始數據類型(如“[]”)時,以下是在內存中發生的情況:
為變量創建唯一標識符(“myArray”)
在內存中分配一個地址(將在運行時分配)
存儲在堆上分配的內存地址的值(將在運行時分配)
堆上的內存地址存儲分配的值(空數組[])
從這里,我們可以 push, pop,或對數組做任何我們想做的。
myArray.push("first") myArray.push("second") myArray.push("third") myArray.push("fourth") myArray.pop()let vs const
一般來說,我們應該盡可能多地使用const,只有當我們知道某個變量將發生改變時才使用let。
讓我們明確一下我們所說的“改變”是什么意思。
let sum = 0 sum = 1 + 2 + 3 + 4 + 5 let numbers = [] numbers.push(1) numbers.push(2) numbers.push(3) numbers.push(4) numbers.push(5)
這個程序員使用let正確地聲明了sum,因為他們知道值會改變。但是,這個程序員使用let錯誤地聲明了數組 numbers ,因為他將把東西推入數組理解為改變數組的值。
解釋“改變”的正確方法是更改內存地址。let 允許你更改內存地址。const 不允許你更改內存地址。
const importantID = 489 importantID = 100 // 類型錯誤:賦值給常量變量
讓我們想象一下這里發生了什么。
當聲明importantID時,分配了一個內存地址,并存儲489的值。記住,將變量importantID看作等于內存地址。
當將100分配給importantID時,因為100是一個原始數據類型,所以會分配一個新的內存地址,并將100的值存儲這里。
然后 JS 嘗試將新的內存地址分配給 importantID,這就是拋出錯誤的地方,這也是我們想要的行為,因為我們不想改變這個 importantID的值。
當你將100分配給importantID時,實際上是在嘗試分配存儲100的新內存地址,這是不允許的,因為importantID是用const聲明的。
如上所述,假設的初級JS程序員使用let錯誤地聲明了他們的數組。相反,他們應該用const聲明它。這在一開始看起來可能令人困惑,我承認這一點也不直觀。
初學者會認為數組只有在我們可以改變的情況下才有用,const 使數組不可變,那么為什么要使用它呢? 請記住:“改變”是指改變內存地址。讓我們深入探討一下為什么使用const聲明數組是完全可以的。
const myArray = []
在聲明 myArray 時,將在調用堆棧上分配內存地址,該值是在堆上分配的內存地址。堆上存儲的值是實際的空數組。想象一下,它是這樣的:
如果我們這么做:
myArray.push(1) myArray.push(2) myArray.push(3) myArray.push(4) myArray.push(5)
執行 push 操作實際是將數字放入堆中存在的數組。而 myArray 的內存地址沒有改變。這就是為什么雖然使用const聲明了myArray,但沒有拋出任何錯誤。
myArray 仍然等于 0458AFCZX91,它的值是另一個內存地址22VVCX011,它在堆上有一個數組的值。
如果我們這樣做,就會拋出一個錯誤:
myArray = 3
由于 3 是一個原始數據類型,因此生成一個新的調用堆棧上的內存地址,其值為 3,然后我們將嘗試將新的內存地址分配給 myArray,由于myArray是用const聲明的,所以這是不允許的。
另一個會拋出錯誤的例子:
myArray = ["a"]
由于[a]是一個新的引用類型的數組,因此將分配調用堆棧上的一個新內存地址,并存儲堆上的一個內存地址的值,其它值為 [a]。然后,我們嘗試將調用堆棧內存地址分配給 myArray,這會拋出一個錯誤。
對于使用const聲明的對象(如數組),由于對象是引用類型,因此可以添加鍵,更新值等等。
const myObj = {} myObj["newKey"] = "someValue" // 這不會拋出錯誤為什么這些知識對我們有用呢
JavaScript 是世界上排名第一的編程語言(根據GitHub和Stack Overflow的年度開發人員調查)。 掌握并成為“JS忍者”是我們所有人都渴望成為的人。
任何質量好的的 JS 課程或書籍都提倡使用let, const 來代替 var,但他們并不一定說出原因。 對于初學者來說,為什么某些 const 變量在“改變”其值時會拋出錯誤而其他 const變量卻沒有。 對我來說這是有道理的,為什么這些程序員默認使用let到處避免麻煩。
但是,不建議這樣做。谷歌擁有世界上最好的一些程序員,在他們的JavaScript風格指南中說,使用 const 或 let 聲明所有本地變量。默認情況下使用 const,除非需要重新分配變量,不使用 var 關鍵字(原文)。
雖然他們沒有明確說明原因,但據我所知,有幾個原因
先發制人地限制未來的 bug。
使用 const 聲明的變量必須在聲明時初始化,這迫使程序員經常在范圍方面更仔細地放置它們。這最終會導致更好的內存管理和性能。
要通過代碼與任何可能遇到它的人交流,哪些變量是不可變的(就JS而言),哪些變量可以重新分配。
希望上面的解釋能幫助你開始明白為什么或者什么時候應該在代碼中使用 let 和 const 。
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
你的點贊是我持續分享好東西的動力,歡迎點贊!
交流干貨系列文章匯總如下,覺得不錯點個Star,歡迎 加群 互相學習。
https://github.com/qq44924588...
我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的干貨,在進階的路上,共勉!
關注公眾號,后臺回復福利,即可看到福利,你懂的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103530.html
摘要:它對數組和對象使用按值傳遞,但這是在的共享傳參或拷貝的引用中使用的按值傳參。例如在這里,變量和值在執行期間存儲在堆棧中。返回值這是可選的,函數可以返回值,也可以不返回值。變量被推入堆棧,從而在執行時成為的副本。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第 22 篇。 想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你! 如果你錯過了前面的章節,可...
摘要:它對數組和對象使用按值傳遞,但這是在的共享傳參或拷貝的引用中使用的按值傳參。例如在這里,變量和值在執行期間存儲在堆棧中。返回值這是可選的,函數可以返回值,也可以不返回值。變量被推入堆棧,從而在執行時成為的副本。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第 22 篇。 想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你! 如果你錯過了前面的章節,可...
摘要:我們將拆分來分析它的工作原理,更重要的是,它在性能方面如何提升加載時間,執行速度,垃圾回收,內存使用率,平臺訪問,調試,多線程和可移植性。目前,是圍繞和用例設計的。多線程在單個線程上運行。目前不支持多線程。被設計為安全和便攜。 我們將拆分WebAssembly來分析它的工作原理,更重要的是,它在性能方面如何提升JavaScript:加載時間,執行速度,垃圾回收,內存使用率,平臺API訪...
摘要:為了方便大家共同學習,整理了之前博客系列的文章,目前已整理是如何工作這個系列,可以請猛戳博客查看。以下列出該系列目錄,歡迎點個星星,我將更友動力整理理優質的文章,一起學習。 為了方便大家共同學習,整理了之前博客系列的文章,目前已整理 JavaScript 是如何工作這個系列,可以請猛戳GitHub博客查看。 以下列出該系列目錄,歡迎點個星星,我將更友動力整理理優質的文章,一起學習。 J...
摘要:主題來自于的典型面試問題列表。有多種方法來處理事件委托。這種方法的缺點是父容器的偵聽器可能需要檢查事件來選擇正確的操作,而元素本身不會是一個監聽器。 showImg(http://fw008950-flywheel.netdna-ssl.com/wp-content/uploads/2014/11/Get-Hired-Fast-How-to-Job-Search-Classifieds...
閱讀 2955·2021-10-20 13:46
閱讀 2511·2021-08-12 13:22
閱讀 2692·2019-08-30 15:54
閱讀 2335·2019-08-30 15:53
閱讀 540·2019-08-30 13:47
閱讀 3574·2019-08-23 16:56
閱讀 1719·2019-08-23 13:02
閱讀 1790·2019-08-23 12:25