【repost】 JS變量重復聲明以及忽略var 聲明的問題及其背后的原理


JS的容錯率很高,一些其他語言常見的小錯誤JS都能大度得包容,比如給一個方法傳入超出預計的參數、在聲明變量之前使用該變量(變量的聲明提升解決了這個問題)等等,這里我們就要解剖一下JS變量重復聲明以及當我們忽略var使用 a=2來聲明變量時a為全局變量的問題:

[javascript]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. //第一段代碼  
  2. var a = 2;  
  3. var a = 3;  
  4. alert(a);//3  
  5. //第二段代碼  
  6. <span style="font-size:18px;"></span><pre name="code" class="javascript">a = 2;  
  7. alert(a);//2  

 

 

 
        

    這兩段代碼在JS的眼中是完全可行的,JS會默默忽略掉第二個var聲明來將程序繼續執行下去,而且后面聲明的值會覆蓋掉前面聲明的值,而第二段代碼JS會將忽略var的聲明默認聲明為全局變量。這些大家都應該很清楚,但是JS遇到重復聲明時背后到底是怎樣運行的呢?那就關系到了JS的幕后黑手:引擎以及他的左膀右臂:編譯器以及作用域。

 

    在JS代碼運行過程中:

        引擎負責整個代碼的編譯以及運行,編譯器則負責詞法分析、語法分析、代碼生成等工作而作用域則如我們熟知的一樣,負責維護所有的標識符(變量)。

        當我們執行上面的代碼時,我們可以簡單的理解為新變量分配一塊兒內存,命名為a,並賦值為2,但在運行的時候編譯器與引擎還會進行兩項額外的操作:判斷變量是否已經聲明:

        1.首先編譯器對代碼進行分析拆解,從左至右遇見var a,則編譯器會詢問作用域是否已經存在叫a的變量了,如果不存在,則招呼作用域聲明一個新的變量a,若已經存在,則忽略var 繼續向下編譯,這時a = 2被編譯成可執行的代碼供引擎使用。

        2.引擎遇見a=2時同樣會詢問在當前的作用域下是否有變量a,若存在,則將a賦值為2(由於第一步編譯器忽略了重復聲明的var,且作用域中已經有a,所以重復聲明會發生值得覆蓋而並不會報錯)。若不存在,則順着作用域鏈向上查找,若最終找到了變量a則將其賦值2,若沒有找到,則招呼作用域聲明一個變量a並賦值為2(這就是為什么第二段代碼可以正確執行且a變量為全局變量的原因,當然,在嚴格模式下JS會直接拋出異常:a is not defined)。

       雖然JS很勤勞,可以幫我們解決一些小問題,但是作為程序員的我們最好按照代碼規范來進行書寫,於人於己都大有裨益,何樂而不為呢。

       注:關於a = 2 a會被聲明為全局變量其中涉及到LHS查詢方式,如需了解請移步至本人另一篇文章:JS引擎之LHS RHS

 在書寫代碼的時候我們無時無刻不在與作用域較勁,而引擎是如何在沿着作用域鏈把我們想要的東西查找出來的呢?這里就涉及到了L與R的區別。

    通過字面意思就很容易理解L代表left R代表right,而LHS與RHS查詢我們可以先簡單的區分為:查詢在 = 號左邊的變量時,引擎使用LHS,查詢在 = 右邊的變量時,引擎使用RHS。LHS查詢出來的是變量的地址,方便進行形如a = 2的賦值操作,因為引擎根本不需要關心a里面存的是什么鬼,按照程序猿的要求把2塞給a就可以了,而RHS查詢出來的是變量存儲的值,以便形如 a = b的賦值操作,引擎同樣不需要關心 b 放在內存的哪個“格子”,只需要知道格子里面放的什么就可以了。

    當然, 根據 = 左右來區分LHS RHS是不全面的,因為我們很容易漏掉一些隱藏的LHS與RHS:

[javascript]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. var c =3;  
  2. function a(b){  
  3.     console.log(b+c);  
  4. }  
  5. a(2);  

    在上面一段代碼中,我們可以很明顯的得出 c ...使用了LHS,console.log()中的b、c使用了RHS,但是在調用函數a、console.log的時候同樣使用了RHS,參數b的賦值也同樣使用了LHS,所以我們最好通過取值、取地址這兩個行為來判斷引擎使用的查詢方式。

 

    對於上面的代碼,引擎與作用域是這樣交流的:

    引擎:全局作用域,我想找一下c,你見過他么?

    全局作用域:嗨,別提了,編譯器那小子剛剛聲明了它,拿去吧!

    引擎:太棒了!我現在要把3丟給他

    引擎:等一下,還有一個事情想麻煩你一下,我想對a函數進行引用,你知道她在哪里么?

    全局作用域:就是那個跟c一起被丟進來的家伙把?喏,在這里呢,給你。

    引擎:哈哈,太感謝了,這樣我只需要把2.....。

    a函數作用域:薩瓦迪卡,引擎,今天天氣不錯啊,一起出去玩吧!

    引擎:別提了,我手頭上忙的要死,對了,你碰到過一個叫b的么?

    a函數作用域:哦,他是a函數的一個形參,我這剛好有,拿去用

    引擎:夠哥們,這樣我只需要把2放到b里面,之后.....哎,小a,console你有么

    a函數作用域:有有有,這是個內置對象,給你

    引擎:哈哈,你一直這么靠譜,我找找,哎呦,真有log這個函數,我得趕緊引用他

    引擎:你看我這腦子,你能在幫我找一下b么,我得確認一下b的內容

    a函數作用域:放心,看!b沒有變過,放心

    引擎:那最好了,就差最后一步了,做完喝酒去,你那里有沒有c,交出來我請你一包辣條!

    a函數作用域:真的么!我找找,嗯.....不行,我這里沒有,你得去問問我大哥 全局作用域

    引擎:全局作用域,不好意思,又來找你了,不知道你有沒有c,我拿辣條跟你換

    全局作用域:看你累的滿頭大汗的,辣條你自己留着吧,c給你,做完快歇歇吧

    引擎:么么噠,你最好了,晚上我請你吃飯!

    看完上面的對話,不知道你對LHS RHS是否有了足夠的了解,還有一點需要注意的就是,當查找到全局作用域時,若還沒有查找到要找的變量信息,若為LHS查詢,會默認聲明一個與請求的變量同名的全局變量,而RHS則會拋出錯誤,當然,在嚴格模式下,LHS也同樣會報錯,這是需要注意的地方。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM