晨叔週刊,每週一話題,技術每天漲。 前端
本週的話題是JS的內存問題(加入本週話題,請點擊傳送門)。git
圖 話題入口微信
今天的技術晨報來,就來談談JS中變量的,值傳遞和引用傳遞的問題。如今,對於不少的JSer來說,基本不關心堆和棧的問題,代碼照樣666。函數
可是,如今的前端,再也不是傳統的JQ時代,而是MVVM,組件化,工程化。前端的承載着複雜業務邏輯。爲此,內存問題,成爲JSer必需要考慮的問題。 本文從堆棧講起,讓你們理解JS中變量的內存使用以及變更狀況 。組件化
1、初步瞭解堆棧 spa
先初步瞭解JS中的堆和棧,首先,內存空間分爲 堆和棧兩個區域,js 代碼運行時,js解析器會先判斷變量類型,根據變量類型,將變量放到不一樣的內存空間中(堆和棧)。指針
圖 1code
基本的數據類型(String,Number,Boolean,Null,Undefined)都會分配棧區。而Object (對象)類型的變量都放到堆區。對象
以下代碼示例blog
1 var a = 12; 2 var b = false; 3 var c = "string" 4 5 var chenshu = {name:"晨叔週刊",desc:"每週一話題,技術每天漲" }
對應的內存分配圖以下圖2
圖 2
棧區的特色:空間小,數據類型簡單,讀寫速度快,通常由JS引擎自動釋放
堆區的特色:空間大,數據類型複雜,讀寫速度稍遜,當對象不在被引用時,纔會被週期性的回收。
瞭解了內存的棧區和堆區後, 接下來,來看看變量如何在棧區和堆區「愉快的玩耍」。
2、變量傳遞
進入今天的重點,先看看下面的代碼。
var a = 12; var b= a; b = 13;
上面代碼的運行結果,a 變量的值沒變,由於 第二行「b = a」 ,把a的值賦值個b時, 執行的是「複製」的操做,a 和 b 沒有關係。(so easy ,不在多BB),再往下看
1 var chenshu = {name:"晨叔週刊",desc:"每週一話題,技術每天漲" }; 2 var xiaoming = chenshu;
chenshu 不是基礎類型變量, 而是一個對象。
第二行中,「xiaoming = chenshu」,進行也是「複製的操做」, 爲何? 且看下圖分解。
根據代碼,首先, 申明瞭變量「chenshu」 ,以下圖3。
圖 3
接下來, 「xiaoming = chenshu」(複製操做),內存空間圖以下圖4
圖4
在上圖4 中,xiaoming 變量的存儲空間(棧區)複製了 chenshu變量的值,可是這個變量的值,並非一個基礎的數據類型,而是一個堆區的內存地址(指針)。因此操做「xiaoming」變量和操做「chenshu」變量效果都同樣。
劃重點:在JS的變量傳遞中,本質上均可以當作是值傳遞,只是這個值多是基礎數據類型,也多是一個指針,若是是指針,咱們一般就說爲引用傳遞。 JS中比較特殊,不能直接操做對象的內存空間,必須經過指針(所謂的引用)來訪問。
因此,即便是因此複雜數據類型(對象)的賦值操做,本質上也是值傳遞。在往下看看一個例子。
3、函數的形參、實參的值傳遞
看以下代碼。
function setName(user) { user.name = "new name";// 從新設置name 這個屬性 } var chenshu = { name:"晨叔" }; setName(chenshu); console.log(chenshu.name);
講解:
函數 setName 有一個 形參 「user」,沒毛病。
將「chenshu」對象傳給 「setName 」 函數,chenshu 爲實參,也沒毛病。
重點:傳參的過程當中,js引擎將實參chenshu的值(值是對象{name:"晨叔"}的指針)「複製」給形參。即,形參user 和 chenshu變量指向同一個堆內存對象。 沒毛病。 但問題來了,實參和形參在是一個變量?仍是兩個變量? 不少開發者的誤區:認爲 在 setName 函數中改變了形參user的屬性,實參 chenshu的屬性也發生變更,就認爲同一個變量,但真實的狀況是:實際上實參和形參 是兩個變量,只是實參和形參指向同一個堆區的變量而已,見以下圖5
圖5
形參user只是存了對象 { name:"晨叔" }的地址,但在棧區中,user是單獨存在的,並且函數運行完,user 當即被釋放了,爲了更加直觀的說明這個問題。咱們再來分析一段代碼。
1 function setName(user) 2 { 3 user.name = "new name";// 從新設置name 這個屬性 4 user = { name:"西門吹雪" }; 5 } 6 7 var chenshu = { name:"晨叔" }; 8 setName(chenshu); 9 10 console.log(chenshu.name);//輸出 new name
上面這段代碼就能很能說明問題,在「setName」函數中,形參user首先修改實參chenshu對象的name屬性以後, 又從新指向了一個新對象「{ name:"西門吹雪" }」, 但由於 形參user和實參chenshu是兩個對象,形參user指向新對象後,對實參chenshu並無影響,由於實參和形參進行的是值傳遞(複製),實參和形參是兩個獨立在棧區的變量。
本文的分享就到這裏,咱們來作一下總結。
本文的內容只是初步的探索JS的內存問題,更深的乾貨,更精彩的內容,請加入本期的分享話題,點擊傳送門
請關注「晨叔週刊」微信公衆號, 便可每週與晨叔深刻研究一個話題,每週一發布本週話題,週二,週六 早上九點更新週刊內容。晨叔口號: 晨叔週刊,每週一話題,技術每天漲。