天天看點

死磕java_死磕JavaScript-松散類型、js變量存儲模型、變量提升

好久沒來慕課網學習了,上研究所學生之後,發現突然又變回學生後對自己的要求也松很多,開始到處旅遊,做些沒計劃的事情,也很少寫技術部落格了,最近靜下心來開始研究底層的東西,以後就在這寫了,希望能死磕自己,堅持下去。好了,幹貨走起......

什麼是松散類型

JavaScript兩種變量類型的記憶體模型

預加載

變量提升

javascript裡的變量和其他語言有很大的不同,javascript的變量是一個松散的類型,松散類型變量的特點是變量定義時候不需要指定變量的類型,變量在運作時候可以随便改變資料的類型,但是這種特性并不代表javascript變量沒有類型,當變量類型被确定後javascript的變量也是有類型的。

但是在現實中,很多程式員把javascript松散類型了解為了javascript變量是可以随意定義即你可以不用var定義,也可以使用var定義,其實在javascript語言裡變量定義沒有使用var,變量必須有指派操作,隻有指派操作的變量是賦予給window,這其實是javascript語言設計者提升javascript安全性的一個做法。

此外javascript語言的松散類型的特點以及運作時候随時更改變量類型的特點,很多程式員會認為javascript變量的定義是在運作期進行的,更有甚者有些人認為javascript代碼隻有運作期,其實這種了解是錯誤的,javascript代碼在運作前還有一個過程就是:預加載,預加載的目的是要事先構造運作環境例如全局環境,函數運作環境,還要構造作用域鍊,而環境和作用域的構造的核心内容就是指定好變量屬于哪個範疇,是以在javascript語言裡變量的定義是在預加載完成而非在運作時期。

講一個例子來講解:

var a = 1;

function test(){

console.log(a);//undefined

var a = 2;

console.log(a);//2

}

test();

這是一個令人詫異的結果,為什麼第一個彈出框顯示的是undefined,而不是1呢?這種疑惑的原理我描述如下:

一個頁面裡直接定義在script标簽下的變量是全局變量即屬于window對象的變量,按照javascript作用域鍊的原理,當一個變量在目前作用域下找不到該變量的定義,那麼javascript引擎就會沿着作用域鍊往上找直到在全局作用域裡查找,按上面的代碼所示,雖然函數内部重新定義了變量的值,但是内部定義之前函數使用了該變量,那麼按照作用域鍊的原理在函數内部變量定義之前使用該變量,javascript引擎應該會在全局作用域裡找到變量定義,而實際情況卻是變量未定義,這到底是怎麼回事呢?

這裡我要先講一個知識點,就是JavaScript的變量存儲模型。

javascript語言和java語言一樣變量是分為兩種類型:基本資料類型和引用類型。基本類型是指:Undefined、Null、Boolean、Number和String;而引用類型是指對象,是以javascript的對象指的是引用類型。但是實際開發裡如果我們對基本類型和引用類型的差別不是很清晰,就會碰到我們很多不能了解的問題,下面我們來看看下面的代碼:

var str = “Sharpxiajun";

var num = 1;

var xxx;

console.log(str);//運作結果:sharpxiajun

console.log(num);//運作結果:1

console.log(xxx);//運作結果:undefined

當我們使用引用類型時候,結果就和上面完全不同了,大家請看下面的代碼:

var obj1 = new Object();

obj1.name = "obj1 name”;

console.log(obj1.name);// 運作結果:obj1 name

Javascript裡的基本變量是存放在棧區的(棧區指記憶體裡的棧記憶體),它的存儲結構如下圖所示:

死磕java_死磕JavaScript-松散類型、js變量存儲模型、變量提升

javascript裡引用變量的存儲就比基本類型存儲要複雜多,引用類型的存儲需要記憶體的棧區和堆區(堆區是指記憶體裡的堆記憶體)共同完成,如下圖所示:

死磕java_死磕JavaScript-松散類型、js變量存儲模型、變量提升

了解基本類型變量和引用類型變量的存儲結構後,結合上面開始講的預加載的知識點,我們就能分析出開始那個例子的深層原因了。

引子裡的代碼在函數的局部作用域下變量a被重新定義了,在預加載時候a的作用域範圍也就被框定了,a變量不再屬于全局變量,而是屬于函數作用域,隻不過指派操作是在運作期執行(這就是為什麼javascript語言在運作時候會改變變量的類型,因為指派操作是在運作期進行的),是以第一次使用a變量時候,a變量在局部作用域裡沒有被指派,隻有棧區的标示名稱,是以結果就是undefined了。(這也就是js裡的變量提升的原理)

不過指派操作也不是完全不對預加載産生影響,預加載時候javascript引擎會掃描所有代碼,但不會運作它,當預加載掃描到了指派操作,但是指派操作的變量有沒有被var定義,那麼該變量就會被賦予全局變量即window對象。