JavaScript的预编译
2016-07-20 11:07
302 查看
JS一般都是嵌在html页面中使用,以前总认为JS也跟html一样,是完全解释执行,写在前面的代码一定会先执行。然而,事实并非如此。JS的编译和执行过程如下:
在执行之前,会先进行预编译:对function定义的函数对象,会先预编译为活动对象并添加进内存,其值为函数定义本身,如果出现同名的function,后面定义的,会将前面的覆盖;对以var声明的变量,会先为这个变量在内存中分配一个空间,但并不赋值,此时,其值为undefined。
在解释执行阶段:使用function时,会先在内存中编译的活动对象中查找,如果没有,且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找;遇到类似于 var a = ..形式赋值语句时,就为变量a赋值(在赋值前,变量a只是在内存中有一个空间,但值为undefined,所以在赋值前调用,就会报错。)
然后再是一行行执行所有的语句。
下面来看一些例子:
示例一:
以上这个例子,尽管从代码上看,第一次调用hello(),是在hello函数重新定义之前,但是因为在执行代码前,会有预编译,而在预编译阶段,后面定义的hello函数将前面定义的hello给覆盖了,所以,第一次调用hello()时,调用到的是覆盖后的hello,当然也就输出的是javascript了。
示例二:
以上这个例子,在一开始就调了一次f(),如果完全解释执行,那里在调用前没有定义f(),应该会报错。然而有预编译这个过程,在第一次f()调用之前,就已经预编译了一个名为f的函数,可以输出“FFFF”(我认为其实输出“ffff”的也编译了,但由于其是匿名函数,则在预编译时,只是在内存中占了块空间,并没有把其赋值给任何变量),所以直接调用f(),输出的是“FFFF”。此后,遇到var f = function()… 这句时,将一个输出”ffff”的匿名函数赋值给了f,原来编译出来的f函数被覆盖了,所以在此之后调用时,都是输出为“ffff”。当解释执行再次遇到funcion f()定义时,由于在预编译时已经编译了,在解释执行时,对这些语句就不再作任何操作了。
示例三:
预编译针对的是function定义的对象,对于普通的值或对象并不进行预统计,如以上例子,会输出undefined,因为在预编译阶段,只是看到var c = ..的时候,给c在内存中分配了一块空间,但并没有赋值,其值为undefined
示例四:
以上这个例子是将函数的定义和调用分别用两个script标签包起来,从执行的结果,可以看出,f()调用时,会报错“Uncaught ReferenceError: f is not defined”,而g()调用时,可以正常执行。这是因为JS的预编译和执行是分段进行的。先将第一个script标签中的程序预编译执行完之后,再去预编译第二个script标签中的代码,所以调用f()时,还有没f的定义,则会报错;在预编译执行第二个script标签时,第一个script标签中预编译好的对象不会被回收,本例中,就表示g()的定义依然存在,所以在第二个script标签中调用g(),是可以正常执行的。
以上可以看出,其实都是函数或变量名同名引起的,如果可以保证整个项目中没有同名的函数变量,这些问题基本上很好避免。然而,一个项目可能有很多人在开发,同名的几率非常大,这可如何是好。
请看下集:JavaScript命名空间
在执行之前,会先进行预编译:对function定义的函数对象,会先预编译为活动对象并添加进内存,其值为函数定义本身,如果出现同名的function,后面定义的,会将前面的覆盖;对以var声明的变量,会先为这个变量在内存中分配一个空间,但并不赋值,此时,其值为undefined。
在解释执行阶段:使用function时,会先在内存中编译的活动对象中查找,如果没有,且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找;遇到类似于 var a = ..形式赋值语句时,就为变量a赋值(在赋值前,变量a只是在内存中有一个空间,但值为undefined,所以在赋值前调用,就会报错。)
然后再是一行行执行所有的语句。
下面来看一些例子:
示例一:
function hello() { //先定义了一个hello函数 console.log("js"); } hello(); //在调用之前,先进行了预编译,后面定义的hello覆盖了前面定义的,所这里输出“javascript” function hello() { //后面这个定义的hello函数会覆盖前面定义的hello console.log("javasript"); } hello(); //这里调用,当然输出“javascript”
以上这个例子,尽管从代码上看,第一次调用hello(),是在hello函数重新定义之前,但是因为在执行代码前,会有预编译,而在预编译阶段,后面定义的hello函数将前面定义的hello给覆盖了,所以,第一次调用hello()时,调用到的是覆盖后的hello,当然也就输出的是javascript了。
示例二:
f(); //执行前进行了预编译,输出“FFFF” var f = function() { //给变量f赋值了一个匿名函数 console.log("ffff"); } f(); //这里调用的是赋值后的变量f,输出“ffff” function f() { //funcion定义的函数,已预编译,执行时不作处理 console.log("FFFF"); } f(); //这里调用的还是赋值后的变量f,输出"ffff"
以上这个例子,在一开始就调了一次f(),如果完全解释执行,那里在调用前没有定义f(),应该会报错。然而有预编译这个过程,在第一次f()调用之前,就已经预编译了一个名为f的函数,可以输出“FFFF”(我认为其实输出“ffff”的也编译了,但由于其是匿名函数,则在预编译时,只是在内存中占了块空间,并没有把其赋值给任何变量),所以直接调用f(),输出的是“FFFF”。此后,遇到var f = function()… 这句时,将一个输出”ffff”的匿名函数赋值给了f,原来编译出来的f函数被覆盖了,所以在此之后调用时,都是输出为“ffff”。当解释执行再次遇到funcion f()定义时,由于在预编译时已经编译了,在解释执行时,对这些语句就不再作任何操作了。
示例三:
console.log(c); //此时c这个变量是存在的,只是没有赋值,所以输出undefined var c = 2; //赋值在调用之后
预编译针对的是function定义的对象,对于普通的值或对象并不进行预统计,如以上例子,会输出undefined,因为在预编译阶段,只是看到var c = ..的时候,给c在内存中分配了一块空间,但并没有赋值,其值为undefined
示例四:
<script type="application/javascript"> f(); //调用之前,f还没有定义,会报错 function g() { console.log("ggg"); } </script> <script type="application/javascript"> function f() { console.log("fff"); } g(); //调用之前,g已经定义,会正常执行 </script>
以上这个例子是将函数的定义和调用分别用两个script标签包起来,从执行的结果,可以看出,f()调用时,会报错“Uncaught ReferenceError: f is not defined”,而g()调用时,可以正常执行。这是因为JS的预编译和执行是分段进行的。先将第一个script标签中的程序预编译执行完之后,再去预编译第二个script标签中的代码,所以调用f()时,还有没f的定义,则会报错;在预编译执行第二个script标签时,第一个script标签中预编译好的对象不会被回收,本例中,就表示g()的定义依然存在,所以在第二个script标签中调用g(),是可以正常执行的。
以上可以看出,其实都是函数或变量名同名引起的,如果可以保证整个项目中没有同名的函数变量,这些问题基本上很好避免。然而,一个项目可能有很多人在开发,同名的几率非常大,这可如何是好。
请看下集:JavaScript命名空间
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子
- JavaScript 各种遍历方式详解