JS的容錯(cuò)率很高慢洋,一些其他語言常見的小錯(cuò)誤JS都能大度得包容吹泡,比如給一個(gè)方法傳入超出預(yù)計(jì)的參數(shù)盟庞、在聲明變量之前使用該變量(變量的聲明提升解決了這個(gè)問題)等等阶剑,這里我們就要解剖一下JS變量重復(fù)聲明以及當(dāng)我們忽略var使用 a=2來聲明變量時(shí)a為全局變量的問題:
//第一段代碼
var a = 2;
var a = 3;
alert(a);//3
//第二段代碼
<span style="font-size:18px;"></span><pre name="code" class="javascript">a = 2;
alert(a);//2
這兩段代碼在JS的眼中是完全可行的跃巡,JS會(huì)默默忽略掉第二個(gè)var聲明來將程序繼續(xù)執(zhí)行下去,而且后面聲明的值會(huì)覆蓋掉前面聲明的值牧愁,而第二段代碼JS會(huì)將忽略var的聲明默認(rèn)聲明為全局變量素邪。這些大家都應(yīng)該很清楚,但是JS遇到重復(fù)聲明時(shí)背后到底是怎樣運(yùn)行的呢猪半?那就關(guān)系到了JS的幕后黑手:引擎以及他的左膀右臂:編譯器以及作用域兔朦。
在JS代碼運(yùn)行過程中:
引擎負(fù)責(zé)整個(gè)代碼的編譯以及運(yùn)行,編譯器則負(fù)責(zé)詞法分析磨确、語法分析沽甥、代碼生成等工作而作用域則如我們熟知的一樣,負(fù)責(zé)維護(hù)所有的標(biāo)識(shí)符(變量)乏奥。
當(dāng)我們執(zhí)行上面的代碼時(shí)摆舟,我們可以簡單的理解為新變量分配一塊兒內(nèi)存,命名為a,并賦值為2恨诱,但在運(yùn)行的時(shí)候編譯器與引擎還會(huì)進(jìn)行兩項(xiàng)額外的操作:判斷變量是否已經(jīng)聲明:
首先編譯器對(duì)代碼進(jìn)行分析拆解媳瞪,從左至右遇見var a,則編譯器會(huì)詢問作用域是否已經(jīng)存在叫a的變量了胡野,如果不存在材失,則招呼作用域聲明一個(gè)新的變量a,若已經(jīng)存在硫豆,則忽略var 繼續(xù)向下編譯龙巨,這時(shí)a = 2被編譯成可執(zhí)行的代碼供引擎使用。
引擎遇見a=2時(shí)同樣會(huì)詢問在當(dāng)前的作用域下是否有變量a熊响,若存在旨别,則將a賦值為2(由于第一步編譯器忽略了重復(fù)聲明的var,且作用域中已經(jīng)有a汗茄,所以重復(fù)聲明會(huì)發(fā)生值得覆蓋而并不會(huì)報(bào)錯(cuò))秸弛。若不存在,則順著作用域鏈向上查找洪碳,若最終找到了變量a則將其賦值2递览,若沒有找到,則招呼作用域聲明一個(gè)變量a并賦值為2(這就是為什么第二段代碼可以正確執(zhí)行且a變量為全局變量的原因瞳腌,當(dāng)然绞铃,在嚴(yán)格模式下JS會(huì)直接拋出異常:a is not defined)。
雖然JS很勤勞嫂侍,可以幫我們解決一些小問題儿捧,但是作為程序員的我們最好按照代碼規(guī)范來進(jìn)行書寫,于人于己都大有裨益挑宠,何樂而不為呢菲盾。
注:關(guān)于a = 2 a會(huì)被聲明為全局變量其中涉及到LHS查詢方式
在書寫代碼的時(shí)候我們無時(shí)無刻不在與作用域較勁,而引擎是如何在沿著作用域鏈把我們想要的東西查找出來的呢各淀?這里就涉及到了L與R的區(qū)別懒鉴。
通過字面意思就很容易理解L代表left R代表right,而LHS與RHS查詢我們可以先簡單的區(qū)分為:查詢在 = 號(hào)左邊的變量時(shí)碎浇,引擎使用LHS疗我,查詢在 = 右邊的變量時(shí),引擎使用RHS南捂。LHS查詢出來的是變量的地址吴裤,方便進(jìn)行形如a = 2的賦值操作,因?yàn)橐娓静恍枰P(guān)心a里面存的是什么鬼溺健,按照程序猿的要求把2塞給a就可以了麦牺,而RHS查詢出來的是變量存儲(chǔ)的值钮蛛,以便形如 a = b的賦值操作,引擎同樣不需要關(guān)心 b 放在內(nèi)存的哪個(gè)“格子”剖膳,只需要知道格子里面放的什么就可以了魏颓。
當(dāng)然, 根據(jù) = 左右來區(qū)分LHS RHS是不全面的吱晒,因?yàn)槲覀兒苋菀茁┑粢恍╇[藏的LHS與RHS:
var c =3;
function a(b){
console.log(b+c);
}
a(2);
在上面一段代碼中甸饱,我們可以很明顯的得出 c ...使用了LHS,console.log()中的b仑濒、c使用了RHS叹话,但是在調(diào)用函數(shù)a、console.log的時(shí)候同樣使用了RHS墩瞳,參數(shù)b的賦值也同樣使用了LHS,所以我們最好通過取值驼壶、取地址這兩個(gè)行為來判斷引擎使用的查詢方式。
對(duì)于上面的代碼喉酌,引擎與作用域是這樣交流的:
引擎:全局作用域热凹,我想找一下c,你見過他么泪电?
全局作用域:嗨般妙,別提了,編譯器那小子剛剛聲明了它相速,拿去吧股冗!
引擎:太棒了!我現(xiàn)在要把3丟給他
引擎:等一下和蚪,還有一個(gè)事情想麻煩你一下,我想對(duì)a函數(shù)進(jìn)行引用烹棉,你知道她在哪里么攒霹?
全局作用域:就是那個(gè)跟c一起被丟進(jìn)來的家伙把?喏浆洗,在這里呢催束,給你。
引擎:哈哈伏社,太感謝了抠刺,這樣我只需要把2.....。
a函數(shù)作用域:薩瓦迪卡摘昌,引擎速妖,今天天氣不錯(cuò)啊,一起出去玩吧聪黎!
引擎:別提了罕容,我手頭上忙的要死,對(duì)了,你碰到過一個(gè)叫b的么锦秒?
a函數(shù)作用域:哦露泊,他是a函數(shù)的一個(gè)形參,我這剛好有旅择,拿去用
引擎:夠哥們惭笑,這樣我只需要把2放到b里面,之后.....哎生真,小a,console你有么
a函數(shù)作用域:有有有沉噩,這是個(gè)內(nèi)置對(duì)象,給你
引擎:哈哈汇歹,你一直這么靠譜屁擅,我找找,哎呦产弹,真有l(wèi)og這個(gè)函數(shù)派歌,我得趕緊引用他
引擎:你看我這腦子,你能在幫我找一下b么痰哨,我得確認(rèn)一下b的內(nèi)容
a函數(shù)作用域:放心胶果,看!b沒有變過斤斧,放心
引擎:那最好了早抠,就差最后一步了,做完喝酒去撬讽,你那里有沒有c蕊连,交出來我請(qǐng)你一包辣條!
a函數(shù)作用域:真的么游昼!我找找甘苍,嗯.....不行,我這里沒有烘豌,你得去問問我大哥 全局作用域
引擎:全局作用域载庭,不好意思,又來找你了廊佩,不知道你有沒有c囚聚,我拿辣條跟你換
全局作用域:看你累的滿頭大汗的,辣條你自己留著吧标锄,c給你顽铸,做完快歇歇吧
引擎:么么噠,你最好了料皇,晚上我請(qǐng)你吃飯跋破!
看完上面的對(duì)話簸淀,不知道你對(duì)LHS RHS是否有了足夠的了解,還有一點(diǎn)需要注意的就是毒返,當(dāng)查找到全局作用域時(shí)租幕,若還沒有查找到要找的變量信息,若為LHS查詢拧簸,會(huì)默認(rèn)聲明一個(gè)與請(qǐng)求的變量同名的全局變量劲绪,而RHS則會(huì)拋出錯(cuò)誤,當(dāng)然盆赤,在嚴(yán)格模式下贾富,LHS也同樣會(huì)報(bào)錯(cuò),這是需要注意的地方牺六。