前言html
經過本文,你大概明白做用域,做用域鏈是什麼,畢竟這也算JS中的基本概念。面試
一.做用域(scope)函數
什麼是做用域,你能夠理解爲你所聲明變量的可用範圍,我在某個範圍內申明瞭一個變量,且這個變量能在這個範圍內可用, 那麼我能夠說此範圍就是該變量的做用域。post
做用域通常分爲局部做用域和全局做用域。url
怎麼理解呢,先說局部做用域。假設我在A範圍內用var申明瞭一個變量a,變量a只能在範圍A內使用,超出A範圍就調用不到,那麼這樣的變量a就是局部變量,範圍A就是所說的局部做用域。spa
1 function A(){ 2 var a = 1; 3 console.log(a); 4 } 5 A();//1 6 console.log(a);//報錯,A未定義
假設咱們在全局範圍(沒在任何函數體內)申明瞭變量b,你能夠在任何局部做用域內去使用它,那麼咱們通常稱b爲全局變量,包含b的範圍就是全局做用域,b隨處可用,沒任何限制。.net
1 var b = 1; 2 function B(){ 3 console.log(b); 4 } 5 B();//1 6 console.log(b);//1
咱們在上面說,在A範圍內用var申明變量a,a爲局部變量,當咱們不用var申明時,即便在一個局部做用域內,它依舊屬於全局變量。code
1 function A(){ 2 a = 1; 3 console.log(a); 4 } 5 A();//1 6 console.log(a);//1
只要一個變量申明前面未加任何申明符,那此變量就是全局變量,咱們不多這樣去作,由於很難保證後期維護不會致使變量申明重名,這樣的作法容易形成全局污染。htm
順帶一提,在函數體內,局部變量的優先級高於同名的全局變量(函數形參也是局部變量),以下:blog
1 var a = 1; 2 function A(a){ 3 console.log(a); 4 } 5 A(10);//10
固然,函數的局部變量會被已存在的局部變量所覆蓋:
1 function A(a){ 2 var a = 1; 3 console.log(a); 4 } 5 A(10);//1
有一點須要注意的是,咱們在實際開發中每每會遇到做用域嵌套,其實只要清楚做用域間變量是否能使用,是否會被覆蓋的關係,就會很清晰了。
1 var a = 1; 2 function A(){ 3 var a = 2; 4 function B(){ 5 var a = 3; 6 console.log(a); 7 } 8 return B(); 9 } 10 A();//3
二.變量提高
準確來講,在ES6中新增的申明符let已經解決了變量提高這種不嚴謹的問題,我在這裏簡單提提,以前也有一篇博文是專門介紹變量提高的。
什麼是變量提高呢,就是說,一個變量在對應的做用域內是隨處可見的,意思是,就算使用在前,申明在後,它依舊可以使用,不會報錯。
具體想了解看這篇文章吧--申明提早,變量申明提早,函數申明提早,申明提早的前後順序
三.做用域鏈
咱們在上面說,做用域也存在嵌套的問題,那麼咱們能夠這樣去理解做用域鏈,當咱們須要某個變量的值時,咱們先去理它最近的做用域去找,若是找不套,就找它的上級做用域,依次類推,直到找到全局,若是全都未定義,那就拋出一個錯誤,以下。
1 var a = 1 2 function A(){ 3 function B(){ 4 console.log(a); 5 } 6 return B(); 7 } 8 A();//1
能夠說,做用鏈的尋找過程是從內向外的過程,而不是從外到內,能夠站在局部做用域去調用全局做用域的屬性,反過來是不容許的。
OK,概念大概說到這裏吧,嗯,仍是來幾道題目鞏固下。
題目一
1 var a=10; 2 function A(){ 3 alert(a); 4 }; 5 function B(){ 6 var a=20; 7 A(); 8 } 9 B();//10
爲何輸出10,而不是20?js中變量的做用域鏈與定義時的環境有關,與執行時無關。
咱們調用函數B後,函數B又調用了函數A,函數A裏面沒定義變量a可是要用a,函數A只是被B調用且不傳參,所以函數A無權使用函數B的局部變量a,而咱們在上方還有一個全局變量a,所以這裏輸出10.
1 var a=10; 2 function A(a){ 3 alert(a); 4 }; 5 function B(){ 6 var a=20; 7 A(a); 8 } 9 B();//20
這樣就能夠輸出20了,函數B調用函數A的同時傳入參數a,函數B提供了變量a,那就輸出20了。
題目二
1 function aaa(){ 2 a=10; 3 } 4 alert(a);//報錯
想到了一個問題,這裏就將題目稍做修改了。咱們在前面說,在局部做用域內,不用var 申明一個變量,它也是全局變量,隨處可見,那爲什麼在外輸出它會報錯呢?
函數是個很奇怪的東西,能夠這樣理解,一個函數當沒被調用,它實際上是不可見的,它裏面的全部變量也不可見,即便a確實是全局變量沒錯,它仍是在是否可見上受到了函數的限制,調用後就可見了。
1 function A(){ 2 a=10; 3 } 4 A() 5 alert(a);//10
那咱們改爲這樣呢?
1 alert(a);//報錯 2 function A(){ 3 a=10; 4 } 5 A(); 6 alert(a)
不是說函數調用后里面的變量a就隨便用了嗎,怎麼上面的又報錯了?由於代碼都有本身的執行順序,要知道第一次alert a,此時函數還未被調用,因此就報錯了,有點繞,試着理解。
就整理這麼多吧,但願有所幫助。
本文思路參考了 博文