JS做用域,做用域,做用鏈詳解

前言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,此時函數還未被調用,因此就報錯了,有點繞,試着理解。

  就整理這麼多吧,但願有所幫助。

本文思路參考了 博文

JS做用域面試題總結

js做用域與原型的筆試題

相關文章
相關標籤/搜索