在瞭解做用域以前,先來看一個問題;
var xixi= 222;
在js處理這句話的時候,會發生什麼呢?java
*##代碼處理過程框架
![](http://images2015.cnblogs.com/blog/1044766/201707/1044766-20170713173845525-1963323558.jpg)
##做用域是什麼呢
咱們將做用域定義爲一套規則,用來管理引擎是如何在當前做用域以及嵌套的子做用域中根據標識符名稱進行變量查找.
##做用域嵌套
* 當一個函數嵌套在另外一個函數中的時候,就發生了做用域的嵌套,所以,在當前做用域中沒法找到某個變量的時候,引擎就會在外層嵌套的做用域中繼續查找,直到找到該變量,或者到達最外層做用域.函數
* 將做用域鏈比做建築的話:一樓就表明當前的做用域,若是找不到變量,就會往上爬樓,依次尋找變量,直到最頂層仍找不到變量的話,就會報出錯誤. ![](http://images2015.cnblogs.com/blog/1044766/201707/1044766-20170713173857212-898722737.jpg)
能夠將內部的變量和函數定義隱藏起來,外部做用域沒法訪問包裝函數內部的任何內容
若是外部有一個變量xi,可是在函數做用域中也想用這個名字該怎麼辦呢?
java var xi = '外部'; function foo(){ var xi = '內部'; console.log(xi) //'內部' } foo() console.log(xi); //'外部'
可是所定義的變量foo可能污染了它當前所在的做用域,且執行這個函數,必需要再加一行代碼foo()來調用;優化一下;
java var xi = '外部'; (function foo(){ var xi = '內部'; console.log(xi) //'內部' })() console.log(xi); //'外部'
若是咱們想在內部也訪問外部的相同變量,該怎麼區分和訪問呢?優化
var xi = '外部'; (function foo(global){//這裏能夠將後面傳來的參數從新命名; var xi = '內部'; console.log(xi) //'內部' console.log(global.xi) //內部 })(window) //這裏只要把window對象做爲一個參數傳給封裝函數就好了. console.log(xi); //'外部'
不少框架源碼都用了這個方式來解決諸如防止undefined被篡改等狀況
函數是js中最多見的做用域單元code
直覺上,咱們會認爲js在執行代碼的時候是由上到下一步一步執行的,可是實際上並不徹底是這樣
咱們先看看一段代碼對象
a = 2; var a; console.log(a); //2
這句話的代碼等同於blog
var a; a= 2; console.log(2)
再來看一段代碼:作用域
console.log(a);//undefined var a = 2;
這段代碼等同於:編譯器
var a; console.log(a); a = 2;
在這段代碼裏到底發生了什麼呢?源碼
js的做用域基本上都是詞法做用域;什麼是詞法做用域呢?就是定義在詞法階段的做用域,是由你在寫代碼的時候將變量和做用域寫在哪來決定的.大部分狀況下詞法做用域是不變的.
咱們以前提到過,編譯器第一個工做就是把代碼解析成詞法單元,而後語法分析成語法樹;在這期間,編譯器基本可以知道標識符是在哪聲明以及是怎麼聲明的.它會用合適的做用域將他們關聯起來.
回到咱們剛剛的代碼;```java console.log(a);//undefined var a = 2; ``` 在編譯階段,會執行
var a;;剩餘的
a = 2`會留在原地等待執行;所以等同於
java var a; console.log(a); a = 2;
在這個階段,變量聲明會從他們代碼所在的位置移動到最頂層更,這個過程就叫作提高;
foo(); // 1 var foo;//儘管此變量聲明在function foo()以前,可是由於函數優先 //foo變量聲明就會變成重複聲明,忽略; function foo(){ console.log(1); } foo = function(){ console.log(2); }
這段代碼會被引擎理解爲:
function foo(){ console.log(1) } foo(); foo = function(){ console.log(2) }
雖然var重複聲明會被忽略,可是後面的函數聲明會覆蓋前面的
foo(); // 3 function foo(){ console.log(1); } var foo = function(){ console.log(2); } function foo(){ //覆蓋了以前的function foo(); console.log(3); }