废话不说,直接上代码(这是在JavaScript面对对象编程指南上面看到的一个例子)
var a=123; function f(){ alert(a); var a=1; alert(a); } f();
书上的解释是这样的:当JavaScript执行过程进入新函数时,这个函数内被声明的所有变量都会被移动导到函数最开始的地方。这种现象叫做提升。且被提升的只有变量的声明。
上面这个例子可以等价于:
var a=123;function f(){ var a; alert(a); a=1; alert(a); }f(); //答案很显然是1
书上只是把它当做一个定义,告诉读者必须记住。其实我们可以从JavaScript编译器的原理出发,搞清楚这段代码到底是怎么编译的就不用死记了!!
那么我们就首先简单的谈一下JavaScript代码是怎样编译执行的(浅显的谈)首先JavaScript代码的编译执行主要由JavaScript引擎,编译器,作用域三个负责。
引擎:从头到尾负责整个JavaScript程序的编译和执行过程;
编译器:负责语法分析及代码生成等;
作用域:负责收集并维护由所有声明的变量组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些变量的访问权限。
可能不是很明白,我们先看一下传统的编译器编译的过程,一段代码执行大概需经过下面三个过程(其实际过程肯定比这复杂很多):
1,词法分析:将由字符组成的字符串分解成有意义的代码块。而这些代码块被称作词法单元。例如:var a=2;这段代码会被分解成var,a,=,2,;这些词法单元。
2,语法分析:将词法单元流转换成一个由元素逐级嵌套所组成的代表了程序语法结构的“抽象语法树”;
3,代码生成:将“抽象语法树”转换成为可执行代码的过程。这个过程与语言和目标平台相关。
由于JavaScript不同于其他有专用编译器的语言如C(如hello.c是先编译成可执行的hello.0程序,这儿阶段声明赋值都已经操作完成,如果编译成功就能执行。)而JavaScript代码编译阶段只是一个对变量的声明阶段,其赋值什么的操作是在代码实际执行的时候才进行的。这也是为什么JavaScript代码写完后必须执行才能知道有没有错误。
好了相关概念介绍基本差不多了,那上面这段代码该如何用编译器去理解呢!我们先来简单的理解var a=2;这段代码。首先这段代码由交给编译器:编译器工作就是负责语法分析,它首先会对var a进行声明,然后去它的作用域中寻找之前有没有声明a,如果已经声明了就忽略这个声明,如果没有找到就在作用域中声明一个新的变量a,然后编译器就将代码编译成引擎可以运行的代码,这些代码用来处理a=2;引擎拿到代码后首先会在作用域中寻找有没有a变量,如果有就使用这个变量a,如果没有就会抛出错误。这段过程总结一下就是代码首先在编译器中声明相关变量,然后交给引擎,引擎便对这些变量进行操作(赋值等)。
有了上面的分析过程我们再来看第一段代码:书上之所以能把它改写成下面那段代码,就是因为这段代码首先会在编译器进行编译,编译的过程就是将这段代码中作用域相关的各个变量进行声明,这是在引擎执行代码前工作的,所以改写的时候var a会提到作用域的最前面,等编译完成后JavaScript引擎才会进一步执行代码也就是对变量进行各种赋值等操作。这个过程就是书上面提到那个词“提升”。其他的一些细节就不用赘述了。第二段代码应该能很清楚的理解了。(注意:提升只能在变量自己的作用域里面提升,不能越过作用域提升到外面来。)。
再给一个练习的例子:
f(); //Hello World!var f;function f(){ aler("Hello World!"); }f=function(){ alert("Hello Javascript!");}
改写形式我就不写了,自己完全可以写出来。