每一個函數都有本身的做用域,當執行流進入一個函數時,函數就會被推入棧中,而在函數執行以後,棧將其執行環境彈出,把控制權放回給以前的做用域,全局做用域是最外圍的一個做用域,所以,全部全局變量和函數都是做爲window對象的屬性和方法建立的。在某個方法函數的做用域中,全部代碼執行完以後,該做用域被銷燬,保存在其中的全部變量和函數定義也會隨着被銷燬,這就是局部做用域。javascript
(PS:全局做用域直到應用程序退出,例如關閉網頁活瀏覽器,纔會被銷燬。)java
我我的理解的做用域鏈就是,當你聲明一個函數時,局部做用域一級一級往上扣起來,就是做用域鏈。瀏覽器
如圖:閉包
做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。函數
好了好了~ 書面語言終於說完了。咱們仍是來看看代碼吧~spa
先來個基礎點的:prototype
var color = "blue"; function changeColor(){ var anothercolor = "red"; if(color==="blue"){ color = anothercolor; } //這裏能夠訪問anothercolor,color } //這裏只能夠訪問color changeColor(); console.log(color);//red console.log(anothercolor);// undefined
解析:函數changeColor()的做用域鏈包含兩個對象,它本身的變量對象和全局環境的變量對象。全局環境的變量對象(即window的對象)有color,changColor(),而changeColor()的變量對象是color,anothercolor。對象
(用我本身的話來解釋就是:父級不能訪問子級的變量,可是子級能夠父級的變量,祖父的變量,曾祖父的變量......哈哈~。而且咱們執行流的訪問順序也是從子級開始,一級一級往上查找你的須要的變量,最後一級就是全局變量。)blog
來看一下做用域鏈的典型栗子:ip
var x = 10; function foo(){ var y = 20; function bar(){ var z = 30; console.log(x+y+z); }; bar(); }; foo();//60
解析:上面代碼的輸出結果是「60」,函數bar()能夠直接訪問"z",而後經過做用域鏈訪問上層的「x」和「y」。
大概基本概念弄清楚了吧?!~~
那我要開始講一些注意事情。。
JS沒有塊級做用域。
說明:在其餘類C語言中,由{花括號}封閉的代碼塊都有本身的做用域,可是隨和親切的JS並不是如此。
舉個栗子:
if(true){ var color = "blue"; } alert(color);//blue
解析:若是在C,C++,或JAVA中,color會在if語句執行完畢被銷燬。但在JS中,if語句中的變量聲明會將變量添加到當前的做用域(這裏的全局環境)。
四不四以爲有點鬱悶?怎麼color這個變量還存在,不該該執行完以後,被銷燬了嗎?
一開始小編也是很鬱悶,再想一想JS的執行流,小編目前的理解是:JS一切皆對象,只有函數方法能夠封裝,也只有函數的執行須要被調用,因此每一個函數方法都有本身的做用域,而像if和for循環等等,這些直接執行的引用函數,所定義的變量都會被添加在與它同兄弟級的做用域中。
如今再來看個栗子:
for(var i = 0 ; i < 10;i++){ doSomething(i); } alert(i);//10
解析:由for語句建立的變量i即便在for循環執行結束後,也依舊會存在於循環外部的執行環境中。
結合做用域看閉包
在JS中,閉包和做用域有緊密的關係。相信你們對下面的閉包栗子必定灰常熟悉,代碼中經過閉包實現了一個簡單的計算器。
function counter(){ var x = 0; return{ increase:function increase(){return ++x;}, decrease:function decrease(){return --x;} }; } var ctor = counter(); console.log(ctor.increase());//1 console.log(ctor.decrease());//0
解析:四不四又納悶了,怎麼counter()函數退出了執行上下文棧,可是變量x尚未被銷燬。閉包有三大特性:1.函數嵌套函數 2.函數內部能夠引用外部的參數和變量 3.參數和變量不會被垃圾回收機制回收。這裏很明顯,counter()函數利用閉包,把變量x引用到全局變量中。當var ctor = counter(),執行完畢後,ctor={increase:function(){...},decrease:function(){...}},這裏須要注意的是,counter雖然退出了執行上下文棧,可是由於ctor中的成員仍然引用counter 的活動變量,因此counter的變量x依然在做用域中。
(附上我的對閉包的理解:在函數嵌套函數中,子函數獲取父函數的私有變量,經過return引用到外部的做用域中。)
做用域鏈的主要做用就是用來變量查找,可是在JS中還有原型鏈的概念。
因而對於二維做用域鏈查找能夠總結爲:當代碼須要查找一個屬性或者描述符的時候,首先會經過做用域鏈來查找相關的對象,一旦對象被找到,就會根據對象的原型鏈來查找屬性。
下面來舉個栗子:
var foo = {} function baz(){ Object.prototype.a = 'Set foo.a from prototype'; return function inner(){ console.log(foo.a); } } baz()();//Set foo.a from prototype
解析:對於這個栗子,流程是這樣的,在inner()並無發現foo,就經過做用域鏈去baz查找,也沒有在baz裏面找到做用域鏈,就去到全局環境,找到了foo,可是並無找到屬性a,因而就去到了foo._proto_的原型鏈中找到了屬性a,便輸出該值。
文章說明:我的查看各類資料,原創所得,觀點不必定準確,歡迎各路大牛勘誤,小女子感激涕零。