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

  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:

  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也同样会报错,这是需要注意的地方。

【repost】 JS变量重复声明以及忽略var 声明的问题及其背后的原理的更多相关文章

  1. JS变量重复声明以及忽略var 声明的问题及其背后的原理

    腾讯的一个笔试题,先看一下 var a = 100; function fn() { alert(a); //undefined var a = 200; alert(a); //200 } fn() ...

  2. es6中的let声明变量与es5中的var声明变量的区别,局部变量与全局变量

    自己通过看typescript官方文档里的let声明,与阮一峰老师翻译的的es6学习文档,总结以下三点 1.var声明可以多次重复声明同一个变量,let不行 2.let变量只在块级作用域里面有效果,v ...

  3. 关于Let和var声明变量的区别

    Let是ES6中添加进来的一个关键字,用于声明变量,其法与var声明变量相同,不同点在于其作用域(块级). 举例可以看出其具体差别 for(var i=0;i<5;i++){ console.l ...

  4. C# var声明变量解析

    C# var声明变量解析: 在C#3.0中提供了一种新的声明变量的方式,这就是var. 通过这个关键字,在声明变量时就无需指定类型了,变量类型是在初始化时由编译器确定的.代码如下: var ss = ...

  5. JS变量对象详解

    JS变量对象详解 开年之后工作热情一直不是很高,这几天一直处于消极怠工状态.早上不想起床,起床了不想上班.明明放假之前工作热情还一直很高,一直心心念念的想把小程序项目怼出来,结果休假回来之后画风完全不 ...

  6. 前端高质量知识(三)-JS变量对象详解

    在JavaScript中,我们肯定不可避免的需要声明变量和函数,可是JS解析器是如何找到这些变量的呢?我们还得对执行上下文有一个进一步的了解. 在上一篇文章中,我们已经知道,当调用一个函数时(激活), ...

  7. js中var的有或无--重复声明和以后的声明

    js中var的有或无--重复声明和以后的声明 使用var语句多次声明一个变量不仅是合法的,而且也不会造成任何错误. 如果重复使用的一个声明有一个初始值,那么它担当的不过是一个赋值语句的角色. 如果重复 ...

  8. js 变量声明 (var使用与不使用的区别)

    js 变量声明 (var使用与不使用的区别) 一.总结 一句话总结:不使用var声明变量的时候,变量是全局对象(window对象)属性,在全局中使用var声明变量是全局变量 var 全局变量 局部变量 ...

  9. js中当for循环中有事件要使用循环变量时,变量用var声明和let声明的区别

    var 声明一个全局变量,声明的变量会变量提升: let 声明一个局部变量: 当页面加载完后,for循环也结束了,如果用var声明的变量此时也随着for循环的结束而自增到满足结束循环的条件, 此时调用 ...

随机推荐

  1. 微信小程序--录音

    var app = getApp(), $ = require("../../utils/util.js"); const recorderManager = wx.getReco ...

  2. [转]一千行 MySQL 学习笔记

    原文地址:https://www.cnblogs.com/shockerli/p/1000-plus-line-mysql-notes.html /* 启动MySQL */ net start mys ...

  3. 基于springboot的SSM框架实现返回easyui-tree所需要数据

    1.easyui-tree easui-tree目所需要的数据结构类型如下: [ { "children": [ { "children": [], " ...

  4. IDEA报错No Spring WebApplicationInitializer types detected on classpath

    IDEA报错No Spring WebApplicationInitializer types detected on classpath https://my.oschina.net/sprouti ...

  5. vim 自动添加作者、版权、修改时间等信息

    相信大家阅读代码时都见过这样的文件头: # THIS FILE IS PART OF LibreBoot PROJECT (归属) # reboot.py - The core part of the ...

  6. QT Designer基础——登录界面设计基础版

    认识QT Designer提供的可选控件:以下八个大类 Layouts:布局相关 Spacers:留空 Buttons:可点击的按钮类 Item Views和 Item Widgets:高级控件,例如 ...

  7. zigbee_蓝牙_wifi的比较与区别分析

    现在无线通读热了起来.三个最大的Wifi.ZigBee.蓝牙它们三个始终困惑着我.那么它们三个有什么区别呢? Zigbee 和蓝牙都是一项无线通信技术.ZigBee的传输距离视发射功率而定,有几百到几 ...

  8. excel 上传读写到数据库

    <HTML> <div class="input-group"> <form id="abc" action="http ...

  9. MySQL8.0安装

    背景 MySQl 8.0 出来已经有段时间了,据说性能有很大提高,在网上看过很多安装教程,大同小异, 在这里亲身实战实战下MySQL8.0在Windows10系统下的安装,以下为详细的安装步骤. 1. ...

  10. Ubuntu16.04 静态IP设置

    为VMware虚拟机内安装的Ubuntu 16.04设置静态IP地址NAT方式 1.安装环境 VMware 12 Ubuntu 16.04 x86_64 2.在VMware中,配置网络环境 VMwar ...