JS作用域和重复声明var背后的原理

JS的容错率很高,一些其他语言常见的小错误JS都能大度得包容,比如给一个方法传入超出预计的参数、在声明变量之前使用该变量(变量的声明提升解决了这个问题)等等,这里我们就要解剖一下JS变量重复声明以及当我们忽略var使用 a=2来声明变量时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会默默忽略掉第二个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查询方式

在书写代码的时候我们无时无刻不在与作用域较劲,而引擎是如何在沿着作用域链把我们想要的东西查找出来的呢?这里就涉及到了L与R的区别。

通过字面意思就很容易理解L代表left R代表right,而LHS与RHS查询我们可以先简单的区分为:查询在 = 号左边的变量时,引擎使用LHS,查询在 = 右边的变量时,引擎使用RHS。LHS查询出来的是变量的地址,方便进行形如a = 2的赋值操作,因为引擎根本不需要关心a里面存的是什么鬼,按照程序猿的要求把2塞给a就可以了,而RHS查询出来的是变量存储的值,以便形如 a = b的赋值操作,引擎同样不需要关心 b 放在内存的哪个“格子”,只需要知道格子里面放的什么就可以了。

当然, 根据 = 左右来区分LHS RHS是不全面的,因为我们很容易漏掉一些隐藏的LHS与RHS:

 var c =3;  
 function a(b){  
 console.log(b+c);  
 }  
 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也同样会报错,这是需要注意的地方。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容