【repost】 JS變數重複聲明以及忽略var 聲明的問題及其背後的原理

來源:互聯網
上載者:User

標籤:pre   keyword   方便   文法分析   detail   mod   維護   使用   編譯器   

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

[javascript] view plain copy  
  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變數為全域變數的原因,當然,在strict 模式下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  
  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則會拋出錯誤,當然,在strict 模式下,LHS也同樣會報錯,這是需要注意的地方。

【repost】 JS變數重複聲明以及忽略var 聲明的問題及其背後的原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.