JS变量重复声明以及忽略var 声明的问题及其背后的原理
腾讯的一个笔试题,先看一下
var a = 100;
function fn() {
alert(a); //undefined
var a = 200;
alert(a); //200
}
fn();
alert(a); //100
var a;
alert(a); //100
var a = 300;
alert(a); //300
前两个很简单,不解释 了,涉及到声明提前的问题。
后面仨为啥呢,这要总结下重复声明的问题:
1.使用var语句多次声明一个变量不仅是合法的,而且也不会造成任何错误.
2.如果重复使用的一个声明有一个初始值,那么它担当的不过是一个赋值语句的角色.
3.如果重复使用的一个声明没有一个初始值,那么它不会对原来存在的变量有任何的影响.
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查询方式,如需了解请移步至本人另一篇文章:JS引擎之LHS RHS
JS变量重复声明以及忽略var 声明的问题及其背后的原理的更多相关文章
- 【repost】 JS变量重复声明以及忽略var 声明的问题及其背后的原理
JS的容错率很高,一些其他语言常见的小错误JS都能大度得包容,比如给一个方法传入超出预计的参数.在声明变量之前使用该变量(变量的声明提升解决了这个问题)等等,这里我们就要解剖一下JS变量重复声明以及当 ...
- es6中的let声明变量与es5中的var声明变量的区别,局部变量与全局变量
自己通过看typescript官方文档里的let声明,与阮一峰老师翻译的的es6学习文档,总结以下三点 1.var声明可以多次重复声明同一个变量,let不行 2.let变量只在块级作用域里面有效果,v ...
- 关于Let和var声明变量的区别
Let是ES6中添加进来的一个关键字,用于声明变量,其法与var声明变量相同,不同点在于其作用域(块级). 举例可以看出其具体差别 for(var i=0;i<5;i++){ console.l ...
- C# var声明变量解析
C# var声明变量解析: 在C#3.0中提供了一种新的声明变量的方式,这就是var. 通过这个关键字,在声明变量时就无需指定类型了,变量类型是在初始化时由编译器确定的.代码如下: var ss = ...
- JS变量对象详解
JS变量对象详解 开年之后工作热情一直不是很高,这几天一直处于消极怠工状态.早上不想起床,起床了不想上班.明明放假之前工作热情还一直很高,一直心心念念的想把小程序项目怼出来,结果休假回来之后画风完全不 ...
- 前端高质量知识(三)-JS变量对象详解
在JavaScript中,我们肯定不可避免的需要声明变量和函数,可是JS解析器是如何找到这些变量的呢?我们还得对执行上下文有一个进一步的了解. 在上一篇文章中,我们已经知道,当调用一个函数时(激活), ...
- js中var的有或无--重复声明和以后的声明
js中var的有或无--重复声明和以后的声明 使用var语句多次声明一个变量不仅是合法的,而且也不会造成任何错误. 如果重复使用的一个声明有一个初始值,那么它担当的不过是一个赋值语句的角色. 如果重复 ...
- js 变量声明 (var使用与不使用的区别)
js 变量声明 (var使用与不使用的区别) 一.总结 一句话总结:不使用var声明变量的时候,变量是全局对象(window对象)属性,在全局中使用var声明变量是全局变量 var 全局变量 局部变量 ...
- js中当for循环中有事件要使用循环变量时,变量用var声明和let声明的区别
var 声明一个全局变量,声明的变量会变量提升: let 声明一个局部变量: 当页面加载完后,for循环也结束了,如果用var声明的变量此时也随着for循环的结束而自增到满足结束循环的条件, 此时调用 ...
随机推荐
- '{}/{}_frames_{:02d}.npy'.format(dataset, train_or_test, i+1)函数
在阅读有关代码的时候,发现一段代码写为: data_frames = np.load(os.path.join(video_root_path, '{}/{}_frames_{:02d}.npy'.f ...
- C# 执行DOS命令和批处理
在项目开发中,有时候要处理一些文件,比如视频格式的转换,如果用C开发一套算法,再用C#调用,未免得不偿失!有时候调用现有的程序反而更加方便.今天就来说一下C#中如何调用外部程序,执行一些特殊任务. 这 ...
- MAC OS进阶必看——这10个技巧让你秒变MAC达人
文章内容及图片来源于:什么值得买,如果涉及版权问题,请联系作者删除 文章收录于:风云社区(提供上千款各类mac软件的下载) 使用mac系统也有好几个年头,出色的办公效率以及越来越广的兼容性让mac成为 ...
- Netty序章之BIO NIO AIO演变
Netty序章之BIO NIO AIO演变 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠的网络服务器和客户端程序.Netty简化了网络程序的开发,是很多框架和公司都在使用 ...
- 主机管理+堡垒机系统开发:strace命令及日志解析(五)
一.strace命令简介 测试命令截图 第一个窗口执行命令如下 [root@elk ~]# w 16:51:56 up 3 days, 6:01, 3 users, load average: 0.0 ...
- 金融量化分析【day111】:Pandas-时间序列处理
一.时间对象处理 1.start 开始时间 df["2018-12-01":"2018-12-30"] 2.end 结束时间 df['2018'] ...... ...
- velocity 新手用小常识--开源,简单易上手
项目中经常用到的 .vm 后缀文件是什么? 基于 java 的 velocity 模版引擎的一种页面控制文件,是一些类似 html 语句和一种叫 VLT 的语句构成 velocity --美 [v ...
- java Concurrent并发容器类 小结
Java1.5提供了多种并发容器类来改进同步容器的性能. 同步容器将所有对容器的访问都串行化,以实现他们的线程安全性.这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重减低. 一 ...
- mpvue体验微信小程序开发
微信小程序 https://developers.weixin.qq.com/miniprogram/introduction/index.html?t=18082114 微信小程序是一种全新的连接用 ...
- Studio 5000指令IN_OUT管脚实现西门子风格
习惯了西门子博途编辑风格的同学,乍一看到Studio 5000的编辑界面,一时不适应,尤其是功能块或指令的IN和OUT管脚在一起,不好分辨,本文简单几步搞定,实现像西门子IN和OUT分左右显示风格. ...