1.編譯原理:javascript
首先,JavaScript是解釋性語言,編譯一行,執行一行html
JavaScript運行三部曲:1.語法分析 2.預編譯 3.解釋執行java
語法分析:js引擎來檢查代碼是否存在語法錯誤閉包
預編譯:簡單理解,就是在內存中開闢一些空間來聲明存放一些變量與函數。函數
<script> alert(fun1()); //能夠成功顯示,說明在執行以前進行了預編譯處理 function fun1(){ return 'OK'; } </script>
<script> alert(num); //顯示undefined,說明在執行以前進行了預編譯,給num分配了內存空間但並不初始化它 var num = 0; </script>
預編譯階段發生變量聲明和函數聲明,沒有初始化行爲(賦值),匿名函數不參與預編譯
只有在解釋執行階段纔會進行變量初始化 性能
3.解釋執行:執行代碼spa
2.做用域:code
定義:做用域就是變量與函數的可訪問範圍,即做用域控制着變量與函數的可見性和生命週期。htm
在JavaScript中,變量的做用域有全局做用域和局部做用域兩種。對象
全局做用域:
//在全局聲明的變量都會在window對象下,能夠經過window對象來訪問,也能夠直接訪問 var name = 'kiki'; console.log(name); console.log(window.name); //效果同樣 //在方法內,給變量加關鍵詞(var)爲局部變量 //不加則是全局變量(注意:必須是在方法執行後,不然該變量爲未定義!!!) function fun1(){ age = 18; alert(age); }; fun1(); console.log(age); //能夠顯示age
局部做用域:
//和全局做用域恰好相反,在函數內部定義的變量只存在於函數的做用域中,其生命週期隨着函數的執行結束而結束 function fun4(){ var hour = 13; console.log(hour); //輸出13 } fun4(); console.log(hour); //報錯 ReferenceError: hour is not defined
例子:var a = 2;
編譯器的處理過程:
1. 遇到 var a ,編譯器會詢問做用域是否已經有一個該名稱的變量存在於同一個做用域的
集合中。若是是,編譯器會忽略該聲明,繼續進行編譯;不然它會要求做用域在當前做
用域的集合中聲明一個新的變量,並命名爲 a 。
2. 接下來編譯器會爲引擎生成運行時所需的代碼,這些代碼被用來處理 a = 2 這個賦值
操做。引擎運行時會首先詢問做用域,在當前的做用域集合中是否存在一個叫做 a 的
變量。若是是,引擎就會使用這個變量;若是否,引擎會繼續查找該變量。
若是引擎最終找到了 a 變量,就會將 2 賦值給它。不然引擎就會舉手示意並拋出一個異
常!
總結:變量的賦值操做會執行兩個步驟,編譯器首先會在當前做用域中聲明一個變量(若是該變量以前未被聲明)
,在執行代碼時,js引擎會在做用域中查找該變量,找到就會給它賦值
而在第二步中,js引擎對於變量的查詢有兩種:LHS與RHS
LHS:找到變量的容器,並對其賦值,var a = 2;就是LHS查詢
RHS:至關於查找某個變量的值,好比:console.log(a);
如下代碼:
function fun2(m){ console.log(m); } fun2(6);
首先在函數調用時,會進行RHS查詢;在參數傳遞的過程當中,至關於給m一個賦值,須要進行LHS查詢;
而後再函數內部,console也進行了一次RHS查詢,console是內置對象;最後查到m的值傳遞進log爲RHS查詢。
3.閉包
要理解閉包首先要理解JavaScript特殊的變量做用域,全局變量和局部變量
在JavaScript中,在函數內部能夠直接讀取全局變量,在函數外面反而不可用讀取內部變量
注意:在函數中若在定義變量時不加關鍵字var,則實際上聲明瞭一個全局變量
function hhh(){ n = 1; } hhh(); console.log(n); //能夠成功訪問到!
固然,有時候須要獲得函數內部的變量,在正常狀況下,是辦不到的,只有變通,那就是:
在函數的內部,再定義一個函數來讀取變量
function aaa(){ var x = 2; function bbb(){ //將bbb做爲返回值,就能夠從外部讀取aaa的變量 console.log(x); } return bbb; }
代碼中的bbb函數,就是一個閉包
閉包的簡單理解:閉包就是可以讀取其它函數內部變量的函數,因爲在JavaScript中,只有函數內部的子函數才能讀取變量,
所以把閉包簡單理解爲「定義在一個函數內部的函數」。因此,在本質上,閉包就是將函數內部和外部相鏈接的一座橋。
閉包的用途:
閉包用途普遍,主要用途有兩個,一個就是能夠讀取函數內部的變量,還有一個就是讓這些變量的值始終保持在內存中,不會被銷燬。
由於與全局變量不一樣的是,定義的變量只存在於函數做用域中,其生命週期會隨着函數的執行結束而結束,即被銷燬。
看一個例子
/* fun2被賦予了一個全局變量glo,致使fun2始終在內存中,而fun2又依存於fun1,因此變量a始終在內存中不會被銷燬 */ function fun1(){ var a = 0; function fun2(){ console.log(a); } return fun2; } var glo = fun1(); glo();
使用閉包的注意點:
①因爲閉包使得函數中的變量都會被保存在內存中,內存消耗很大,全部不能濫用閉包,不然會影響網頁的性能。解決方法,在退出函數以前,將再也不使用的局部變量刪除
②閉包會在父函數的外部,不要隨意改變父函數內部變量的值
閉包概念參考來源:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html