死磕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裡的基本變數是存放在棧區的(棧區指記憶體裡的棧記憶體),它的儲存結構如下圖所示:
javascript裡引用變數的儲存就比基本型別儲存要複雜多,引用型別的儲存需要記憶體的棧區和堆區(堆區是指記憶體裡的堆記憶體)共同完成,如下圖所示:
理解基本型別變數和引用型別變數的儲存結構後,結合上面開始講的預載入的知識點,我們就能分析出開始那個例子的深層原因了。
引子裡的程式碼在函式的區域性作用域下變數a被重新定義了,在預載入時候a的作用域範圍也就被框定了,a變數不再屬於全域性變數,而是屬於函式作用域,只不過賦值操作是在執行期執行(這就是為什麼javascript語言在執行時候會改變變數的型別,因為賦值操作是在執行期進行的),所以第一次使用a變數時候,a變數在區域性作用域裡沒有被賦值,只有棧區的標示名稱,因此結果就是undefined了。(這也就是js裡的變數提升的原理)
不過賦值操作也不是完全不對預載入產生影響,預載入時候javascript引擎會掃描所有程式碼,但不會執行它,當預載入掃描到了賦值操作,但是賦值操作的變數有沒有被var定義,那麼該變數就會被賦予全域性變數即window物件。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4650/viewspace-2798793/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【精】C語言之變數儲存型別C語言變數型別
- js變數提升JS變數
- 前端 -- JS變數提升前端JS變數
- web前端-js變數提升Web前端JS變數
- 關於js變數提升JS變數
- JS——變數提升和函式提升JS變數函式
- 溫故而知新:JS變數提升與時間死區JS變數
- JS 變數儲存?棧 & 堆?NONONO!JS變數
- PHP變數型別PHP變數型別
- Java 變數型別Java變數型別
- 變數型別-Set變數型別
- 淺談變數型別之外的變數命名變數型別
- 變數提升變數
- JS的資料型別和變數(轉)JS資料型別變數
- JavaScript - 變數、值、型別JavaScript變數型別
- 資料型別,變數資料型別變數
- 變數型別轉換變數型別
- Python變數型別Python變數型別
- JS 會有變數提升和函式提升JS變數函式
- js基本語法之 值型別(資料型別)(變數型別)JS資料型別變數
- 死磕JavaScript-垃圾收集機制JavaScript
- 從變數提升角度看待暫時性死區變數
- JavaScript變數提升JavaScript變數
- JavaScript變數儲存淺析(一)JavaScript變數
- JavaScript變數儲存淺析(二)JavaScript變數
- c++中的變數型別_C ++中的變數C++變數型別
- Shell-變數高階用法 (3) 有型別變數變數型別
- 修改全域性變數時,可變型別和不可變型別的區別變數型別
- Dart型別變數-表示資訊Dart型別變數
- c++中變數型別C++變數型別
- 如何判斷變數型別變數型別
- 深入淺出JS - 變數提升(函式宣告提升)JS變數函式
- JS變數宣告和函式宣告提升JS變數函式
- 自定義Report 變數儲存功能變數
- js判斷一個變數是否為字串型別JS變數字串型別
- MYSQL 資料型別儲存-數值型MySQL 資料型別
- 變數提升驗證變數
- 變數提升(hoisting)變數