JavaScript的变量预解析特性
JavaScript是解释型语言是毋庸置疑的,但它是不是仅在运行时自上往下一句一句地解析的呢?事实上或某种现象证明并不是这样的,通过《JavaScript权威指南》及网上相关资料了解到,JavaScript有“预解析”行为。理解这一特性是很重要的,不然在实际开发中你可能会遇到很多无从解析的问题,甚至导致程序bug的存在。为了解析这一现象,也作为自己的一次学习总结,本文逐步引导你来认识JavaScript“预解析”,如果我的见解有误,还望指正。
我们先来看一个例子:
var lastName = "Gonn";
(function DisplayLastName() {
console.log(lastName);
var lastName = "Zeng";
console.log(lastName);
})();//谁能猜出结果是什么?
感觉应该是输出 Gonn 再输出 Zeng。
但结果是 undefined Zeng。为什么呢?
Javascript在执行前会进行类似“预解析”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。
在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = …这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)。
也就是说,解释执行前,先做一遍预解析,给var变量赋值为undefined。当局部变量有var时,这时lastName在函数内部作为局部变量存在。如果上述函数中var lastName=改为lastName=,结果就不一样了。这时预解析时就不会作为局部变量,赋值为undefined。
下面再举一些例子说明下。
function handle(){
alert(arg1);
} ;
handle();
var arg1 = 20;
结果是:undefined。因为在解释到 var arg1 = 20; 这句之前就打印了arg1的值,此时尚未给arg1赋值。
handle();
var handle = function(){
alert(20);
};
结果:handle is not a function. 因为在执行handle()这句时,并没有给handle赋值–函数定义。如果改为:
handle();
var handle = function handle (){
alert(20);
};
在IE下会弹出对话框,因为它将var handle…这句同时解释为函数定义,而函数定义在预编译时就应经有值,所以可以执行。但在FF中,依然只解释为一个变量申明,知道执行到这一句时才会赋值。
正因为如此应避免在变量被初始化之前使用变量。
try/catch的例外,有如下代码:
try{
alert(var1);
alert(varFun);
alert(Fun);
var var1 = 1;
var varFun = function(){};
function Fun(){
alert(1);
}
}
catch(e){
function Fun(){
alert(2);
}
}
以上代码在IE、Chrome中的运行结果是undefined、undefined和fuction(){alert(2);},而在Firefox中的结果是undefined、undefined和“Fun未定义”报错。还不太清楚Firefox对于try/catch的“预解析”是怎么处理的。
1. 如果JavaScript仅是运行时自上往下逐句解析的,下面的代码能正确运行是可以理解的,因为我们先定义函数,然后才调用它。
function showMsg()
{
alert('This is message');
}
showMsg(); // This is message
2. 我们也知道函数可以定义在调用代码之后,如下代码也是能正常工作的。看起来调用showMsg()的时候showMsg()还是没有定义的,但能正常工作,则表明JavaScript是“预解析”的。
showMsg(); // This is message
function showMsg()
{
alert('This is message');
}
3. 上面是函数的例子,下面再来一个普通变量的例子。以下例子运行将会弹出undefined,表明第一句的msg已经是定义了,只是没有初始化,它与var msg; alert(msg);是一样的。如果你把下面第二句注释掉,则会报“msg未定义”错误。这亦表明JavaScript是“预解析”的。
alert(msg); //undefined
var msg='This is message';
4. 再来看一个例子,加深对JavaScript“预解析”印象。以下代码你将看到两次弹出的对话框都是显示This is message 2,为什么会这样呢?其实下面一前一后定义了两个同名函数,后面的showMsg()覆盖了前面定义的(在JavaScript中,同名变量一样会存在覆盖问题),等于第一个showMsg()报废了。为什么第二次调用的showMsg()不是调用它上面定义的那个message 1函数呢?这再次证明JavaScript有“预解析”行为。
showMsg(); // This is message 2
function showMsg()
{
alert('This is message 1');
}
showMsg(); // This is message 2
function showMsg()
{
alert('This is message 2');
}
5. JavaScript“预解析”是把变量或函数预解析到它们能调用的环境(变量运行时环境)中。如下代码看起来alert(msg)之前有看到msg的定义,但是程序运行还是报“msg未定义”错误,这是因为函数里定义的变量是函数的私有变量,外面不能直接调用,这表明JavaScript“预解析”并不是把所有定义的变量统一解析到一个全局对象中,比如window。
function showMsg()
{
var msg='This is message';
}
alert(msg); // msg未定义
6. JavaScript“预解析”是分段进行的,准确说是分<script>块进行的。以下代码出现在同一个页面的两个脚本块中,同时定义了三个同名函数。程序运行结果表明第二个脚本块的showMsg()没有覆盖前面两个showMsg(),而第一个脚本块的第二个showMsg()则覆盖了第一个showMsg()。
<body>
<script type="text/javascript">
showMsg(); //This is message 2
function showMsg()
{
alert('This is message 1');
}
function showMsg()
{
alert('This is message 2');
}
</script> <script type="text/javascript">
function showMsg()
{
alert('This is message 3');
}
</script>
</body>
JavaScript的变量预解析特性的更多相关文章
- javascript作用域、预解析笔记
1.作用域 一般情况下,一段代码中所用到的名字并不总是有效可用的, 而限定这个名字(变量)的可用性的代码范围就是这个名字的作用域,可用有效的减少变量名冲突 2.js的作用域(e ...
- 第112天:javascript中函数预解析和执行阶段
关于javascript中的函数: 1.预解析:把所有的函数定义提前,所有的变量声明提前,变量的赋值不提前 2.执行 :从上到下执行,但有例外(setTimeout,setInterval,aja ...
- vars 变量预解析
JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting(悬置/置顶解析/预解析).当你使用了一个变量,然后不久在函 ...
- 从var func=function 和 function func()区别谈Javascript的预解析机制
var func=function 和 function func()在意义上没有任何不同,但其解释优先级不同:后者会先于同一语句级的其他语句. 即: { var k = xx(); function ...
- 你不知道的JavaScript--Item6 var预解析与函数声明提升(hoist )
1.var 变量预编译 JavaScript 的语法和 C .Java.C# 类似,统称为 C 类语法.有过 C 或 Java 编程经验的同学应该对"先声明.后使用"的规则很熟悉, ...
- JavaScript-----11.预解析
1.预解析 1.1引子 //1问 console.log(num);//报错 num未定义 //2问 console.log(num); //undefined 未报错 var num = 10; / ...
- JS基础研语法---函数基础总结---定义、作用、参数、返回值、arguments伪数组、作用域、预解析
函数: 把一些重复的代码封装在一个地方,在需要的时候直接调用这个地方的代码就可以了 函数作用: 代码重用 函数的参数: 形参:函数定义的时候,函数名字后面的小括号里的变量 实参:函数调用的时候,函数名 ...
- 14 (H5*) JS第4天 函数、作用域、预解析
目录 1:函数的其他定义 2:函数作为参数 3:函数作为返回值 4:作用域 5:作用域链 6:预解析 7:预解析分段 复习 <script> /* * 复习: * 函数:把一些重复的代码封 ...
- 轻松搞定javascript变量(闭包,预解析机制,变量在内存的分配 )
变量: 存储数据的容器 1.声明 var 2.作用域 全局变量. 局部变量. 闭包(相对的全局变量): 3.类型 a.基本类型(undefi ...
随机推荐
- 自动化运维工具saltstack02 -- 之SaltStack的配置管理
SaltStack的配置管理 1.配置管理说明 配置管理,顾名思义及配置与管理, salt-master的配置文件编写格式之YAML语法说明: 数据的结构通过缩进来表示,每一级用两个空格来表示缩进,如 ...
- python-__getattr__ 和 __getattribute__
python3完全使用了新式类,废弃了旧式类,getattribute作为新式类的一个特性有非常奇妙的作用.查看一些博客和文章后,发现想要彻底理解getattr和getattribute的区别,实际上 ...
- CSP201403-3:命令行选项
引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...
- Dubbo使用心得2
- VPS挂机赚美刀详细介绍–Alexamaster操作流程
跟 vps 主机打交道时间长了,手里也渐渐积累了些闲置的 vps.让它们这么闲着吧,感觉有些浪费资源:用起来吧,暂时又没有好的项目.一直听说通过 vps挂机可以赚回主机成本,甚至可以盈利.正好这两天有 ...
- Thunder团队第七周 - Scrum会议6
Scrum会议6 小组名称:Thunder 项目名称:i阅app Scrum Master:苗威 工作照片: 宋雨在照相,所以不在相片中. 参会成员: 王航:http://www.cnblogs.co ...
- windows环境下nginx服务器的安装与配置
转载至:http://www.cnblogs.com/hxxy2003/archive/2012/09/20/2695254.html nginx服务器是一个高性能的HTTP和反向代理服务器,它以稳定 ...
- Linux 环境下svn 服务器搭建
可使用自己下载的svn安装包,但要安装相关依赖包,yum 安装源提供的稳定版本svn 1.yum -y install subversion 2.创建本地库 mkdir -p /var/svn svn ...
- Java 数组转字符
public static String toString(int[] arr){ String temp = ""; for(int i = 0;i<arr.length; ...
- 移动平台的meta标签
这个meta在移动平台上有非常神奇的地方. 1. <meta name="viewport" content="width=device-width; initia ...