JavaScript的“编译时”与“运行时”
JavaScript不是解释型语言吗?为什么会有“编译时”与“运行时”之分?
基础知识
看接下来的内容之前,有几个地方需要先了解。
JavaScript中的“未声明”与"未定义"
看看下面两段代码
代码1.1
alert(a);
</script>
代码1.2
var a;
alert(a);
</script>
第一段代码在各个浏览器中都会提示出错。出错信息是“未定义”或者“not definded"
第二段代码不会提示出错,对话框显示"undifinded"
并且我们如果对变量a使用typeof函数进行检测,会得到同样的结果"undefined"。似乎除了是否报错这一点外,两者都犯了同样的错误就是变量"未定义"。而实际上,这是不一样的。在我看来,前者与后者应该是类似“未声明”与“未定义”的关系。
如果一个变量没有经过显示声明或者出现赋值语句(可以看作隐式声明),那么它就是未声明的,这个时候我们使用它就会报错“未定义”或者“not definded",实际上可以理解为“未声明”。
如果变量经过显示声明但是没有执行过赋值语句,那么它是“未定义”的,也就是确实意义上的"undefined"。我们可以使用它,但是它的默认值是"undefined"。
这一点上,我不知道JavaScript的标准文档中是否有阐明,但在我看来,这是有点混淆的。
JavaScript的作用域
JavaScript的作用域我在前面的文章提过了。
http://www.cnblogs.com/zhengchuyu/archive/2008/07/22/1248286.html
这里再简要提几个要点:
1. 作用域以函数进行划分的,而不是由块(block)划分的。
2. 使用变量的时候将会从当前作用域开始查找其“声明”(隐式或者显式),如果没有找到再向上一级作用域查找。
3. 变量是允许重复定义的,后一个定义将覆盖前一个定义。
4. 函数内部如果不加关键字var而定义的变量,默认为全局变量。
正题:“编译时”与“运行时”
首先要说明的是,JavaScript是否存在编译阶段我不清楚,但是从它的某些表现上来看,我将其分成了“编译时”与“运行时”两个阶段,这在后面内容的理解中我认为是没有问题的。
关于“编译时”与“运行时”,要从 var 关键字与 function 关键字说起。从目前我阅读到的资料和实践中只发现这两个关键字存在这样的特殊性。
var
照例先看代码,我们只需在代码1.2上改变一下语句顺序
代码2.1.1
alert(a);
var a;
</script>
这一段的执行结果与代码1.2的执行结果是一样的。不会报错,窗口提示"undefined"。即我们先前提到的那种“已声明未定义”的情况。由此,我们可以猜到 var a; 这个语句应该是在第一行语句执行之前就执行了的,就像网上有人提到的“预编译”(实际上是不是就不得而知了,待高手指点)。
我们再稍微修改一下代码:
代码2.1.2
alert(a);
var a = 1;
alert(a);
</script>
第一次仍然是提示"undefined",第二次才显示了"1"。因此,实际上只是把声明提前了,但是赋值语句仍然没有改变位置,即可以模拟为:
代码2.1.3
var a;
alert(a);
a = 1;
alert(a);
</script>
我们之前提到这是var关键字的作用,现在我们来验证一下究竟是不是var关键字的作用。在代码2.1.2上删去var关键字
代码2.1.4
alert(a);
a = 1;
</script>
程序报错,也就是前文提到的“未声明”。可以看到没有使用var关键字之后,“预编译”的情况并没有出现。
var关键字的这种特性可能会导致我们的一些细节上的错误,比如我在之前的文章上提出的一个问题(文中称“神奇现象”),后来是博客园上的大虾们帮我解决了,再次感谢他们呵呵。http://www.cnblogs.com/zhengchuyu/archive/2008/07/22/1248576.html
function
function关键字类似的特性貌似要比var关键字稍微复杂一点。但是弄明白了 var 关键字的作用,对function关键字在这上面的表现应该也就不是很难理解了。你将会看到很明显的两段时期“编译时”与“运行时”。
这次我们不像var关键字那样一步一步来了,直接看一段关键代码
代码2.2.1
func(); //2
func = function() {alert(1);};
func(); //1
function func() {alert(2);};
func(); //1
</script>
执行结果我已经标注在代码注释里了。可以看到,function关键字的“预编译”与var关键字的“预编译”稍有不同。它将其“声明”与“定义”一同“预编译”了。代码2.2.1可以模拟为:
代码2.2.2
function func() {alert(2);};
func(); //2
func = function() {alert(1);};
func(); //1
func(); //1
</script>
也就是说,function关键字声明的函数(或者称为“类”或者“变量”我觉得都是可以的),是在“编译时”就执行了。而除了第4行代码之外,其他代码都是在“运行时”执行的,所以我们得到注释里的显示结果。
分段编译
然而JavaScript的“预编译”并不是发生在整个页面的所有脚本中。
有人提到是按<script></script>标签“分段编译”的。在代码2.2.1后面添加多一个<script>块
代码2.3.1
func(); //2
func = function() {alert(1);};
func(); //1
function func() {alert(2);};
func(); //1
</script>
<script type="text/javascript">
function func() {alert(2);};
func(); //2
</script>
执行结果看似证明了上述的结论。第一段第4个语句明显是先于第二段第一个语句执行的。
好像还漏了一些什么东西,想起来再补上去...
欢迎交流前端开发技术!