`

理解Javascript中的预编译

阅读更多
今天工作需要,搜索下JS面试题,看到一个题目,大约是这样的
Js代码

  1. <script>  
  2. var x = 1, y = z = 0;  
  3. function add(n) {  
  4. n = n+1;  
  5.   }  
  6.  
  7. y = add(x);  
  8.  
  9. function add(n) {  
  10. n = n + 3;  
  11. }  
  12.  
  13. z = add(x);  
  14. </script>  

<script>
  var x = 1, y = z = 0;
  function add(n) {
  n = n+1;
  }

  y = add(x);
   
  function add(n) {
  n = n + 3;
  }

  z = add(x);
</script>



问执行完毕后 x, y, z 的值分别是多少?

仔细看的人马上就知道了, x, y 和 z 分别是 1, undefined 和 undefined。

不过,如果将两个 add 函数修改一下,题目变为
Js代码

  1. <script>  
  2. var x = 1, y = z = 0;  
  3. function add(n) {  
  4. return n = n+1;  
  5.   }  
  6.  
  7. y = add(x);  
  8.  
  9. function add(n) {  
  10. return n = n + 3;  
  11. }  
  12.  
  13. z = add(x);  
  14. </script>  

<script>
  var x = 1, y = z = 0;
  function add(n) {
  return n = n+1;
  }

  y = add(x);
   
  function add(n) {
  return n = n + 3;
  }

  z = add(x);
</script>


那么这时 y 和 z 分别是什么呢?我马上想到是 2 和 4,不过结果却是 4 和 4。
这说明,在第一次调用 add 函数之前,第二个 add 函数已经覆盖了第一个 add 函数。原来,这是 JS 解释器的"预编译",JS 解析器在执行语句前会将函数声明和变量定义进行"预编译",而这个"预编译",并非一个页面一个页面地"预编译",而是一段一段地预编译,所谓的段就是一个 <script> 块。且看下面的代码
Js代码

  1. <script>  
  2. function add(n) {  
  3. return n = n+1;  
  4.   }  
  5. alert(add(1));  
  6. </script>  
  7.  
  8. <script>  
  9. function add(n) {  
  10. return n = n+3;  
  11.   }  
  12. alert(add(1));  
  13. </script>  

<script>
  function add(n) {
  return n = n+1;
  }
  alert(add(1));
</script>

<script>
  function add(n) {
  return n = n+3;
  }
  alert(add(1));
</script>



会分别弹出 2 和 4。

那么,将上面的题目再变换一下,如下
Js代码

  1. <script>  
  2. alert( typeof addA);  
  3. addA();  
  4. function addA() {  
  5. alert( "A executed!" );  
  6. };  
  7. </script>  
  8. <script>  
  9. alert( typeof addB);  
  10. addB();  
  11. var addB = function () {  
  12. alert( "B executed!" );  
  13. };  
  14. </script>  

<script>
  alert(typeof addA);
  addA();
  function addA() {
  alert("A executed!");
  };
</script>
<script>
  alert(typeof addB);
  addB();
  var addB = function() {
  alert("B executed!");
  };
</script>



执行结果是什么呢? 按照前面的知识,第一个 <script> 块执行正常,结果就是弹出 "function" 和 "A executed!" 的对话框。
那么第二个 <script> 块呢? 执行结果是弹出 "undefined" 的对话框后报 JS 错误,说 addB 不是一个 function。
有点出乎意料?呵呵,其实第一个 script 块中的 addA 一句是函数声明,当然进行了"预编译",但是第二个 script 块中的 addB 一句并非函数声明。只不过在执行这段 <script> 之前对变量进行了"预声明",因此一开始变量addB是存在的,只不过是 undefined 的(可参看http://eclipse07.iteye.com/admin/blogs/484566 )。因此执行结果便如上面所示。

将题目再变化下,如下
Js代码

  1. <script>  
  2. alert( typeof addB);  
  3. addB();  
  4. var addB = function addB() {  
  5. alert( "B executed!" );  
  6. };  
  7. </script>  

<script>
  alert(typeof addB);
  addB();
  var addB = function addB() {
  alert("B executed!");
  };
</script>


执行结果如何呢?
在 ff 下执行,与上面执行结果一样。打住,且在 IE6 下执行看看如何。
结果是弹出 "function" 和 "B executed!",一切正常。
Google 了一下,有人说这是 IE 的 BUG。

那么,请看下面的代码
Js代码

  1. <script>  
  2. alert( typeof addB);  
  3. var addB = "variable" ;  
  4. function addB() {  
  5. alert( "function addB" );  
  6. }  
  7. alert(addB);  
  8. </script>  

<script>
  alert(typeof addB);
  var addB = "variable";
  function addB() {
  alert("function addB");
  }
  alert(addB);
</script>


执行结果是"function"和"variable"。
JS解析器先预定义了 addB 变量为 undefined, 但是 addB 函数覆盖了此变量,因此一开始执行结果是 function,然后 addB 被赋值为 "variable",因此最后执行结果是 "variable",上面的代码即使变为
Js代码

  1. <script>  
  2. alert( typeof addB);  
  3. function addB() {  
  4. alert( "function addB" );  
  5. }  
  6. var addB = "variable" ;  
  7. alert(addB);  
  8. </script>  

<script>
  alert(typeof addB);
  function addB() {
  alert("function addB");
  }
  var addB = "variable";
  alert(addB);
</script>


结果也一样,这说明JS解析器先预声明变量,再预定义函数 。
小结一下:JS 在执行前会进行类似"预编译"的操作,而且先预定义变量再预定义函数。


分享到:
评论

相关推荐

    JavaScript 详解预编译原理

    大家要明白,这个预编译和传统的编译是不一样的(可以理解js预编译为特殊的编译过程) JavaScript是解释型语言, 既然是解释型语言,就是编译一行,执行一行 传统的编译会经历很多步骤,分词、解析、代码生成...

    实例讲解JavaScript预编译流程

    预编译简单理解就是在内存中开辟一些空间,存放一些变量与函数 ; 2-JS预编译什么时候发生 预编译到底什么时候发生? 误以为预编译仅仅发生在script内代码块执行前 这倒并没有错 预编译确确实实在script代码内执行...

    浅析JavaScript预编译和暗示全局变量

    主要介绍了JavaScript预编译和暗示全局变量的相关资料,帮助大家更好的理解和学习JavaScript,感兴趣的朋友可以了解下

    JavaScript编码规范

    JavaScript一直有着广泛的应用,特别是在...虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。 在线阅读:http://itmyhome.com/js

    JavaScript中变量提升和函数提升的详解

    第一篇文章中提到了变量的提升...(注:当前流行的JS引擎大都对源码进行了编译,由于引擎的不同,编译形式也会有所差异,我们这里说的预编译和提升其实是抽象出来的、易于理解的概念) 下面的代码中,我们在函数中声明了

    精通AngularJS part1

    通过编译函数中的transcludeFn来获取嵌入函数252 通过$transclude在指令控制器中获取嵌入函数253 使用嵌入创建一个if指令253 在指令中使用priority属性255 93理解指令控制器256 为指令控制器注入特殊依赖257 ...

    php网络开发完全手册

    12.5 在JavaScript中调用PHP程序 196 12.5.1 页面的跳转 196 12.5.2 隐性调用PHP程序 196 12.6 JavaScript和PHP的综合实例——表单 12.6 验证 197 12.6.1 表单设计 197 12.6.2 JavaScript代码设计 199 12.6.3 PHP...

    正则表达式

    我们已经发现了,在正则表达式中所有的字母字符和数字都是按照字面意思与自身相匹配的.JavaScript的正则表达式还通过以反斜杠(\)开头的转义序列支持某些非 字母字符.例如,序列 "\n" 在字符串中匹配的是一个直接量换...

    ASP.NET2.0高级编程(第4版)1/6

     程序中使用预编译  的业务对象880 24.2 COM交互操作性:在.NET 中使用COM881 24.2.1 运行库可  调用包装(RCW)881 24.2.2 在ASP.NET代码中  使用COM对象882 24.2.3 错误处理887 24.2.4 用.NET应用程序  部署COM...

    marionette-bootstrap:建立在Marionette之上的模块化Web应用程序体系结构

    使用预编译的模板,以及其他自定义项 我建议您深入研究源代码,以充分理解这一点。 :) 应用程序本身(以及每个模块)需要具有以下必需的实体: Router , Controller和Layout 。 其他应用程序实体是: Models , ...

    2.ASP.NET.2.0.高级编程(第4版) [1/7]

    6.3.3 理解文件大小的限制 173 6.3.4 把上传的文件放在Stream对象中 174 6.3.5 把文件内容从Stream对象移动到Byte数组中 175 6.4 MultiView和View服务器控件 175 6.5 Wizard服务器控件 179 6.5.1 定制边栏导航 ...

    Tcl_TK编程权威指南pdf

    此外,在Xerox PARC工作,那里有许多语言和系统上的专家,我不得不强迫自己去理解Tcl/Tk的长处和弱点。我的许多同事都在他们的项目中采用了Tcl和Tk,但是他们也很快指出了它的缺点。因此,我就总结了一套编程技巧以...

    fis3:FIS3

    如果对FIS先有所了解,但理解不深的,可试着带着这句话去看文档FIS3会在配置文件中给文件添加相应属性,用于控制文件的编译,合并等各种操作;文件属性包括基本属性和插件属性, npm install -g fis3 如果Node版本...

    yliyun3:yliyun3基本fis3

    解决前端工程中性能优化、资源加载(异步、同步、按需、预加载、依赖管理、合并、内嵌)、模块化开发、自动化工具、开发规范、代码部署等问题。 如果对FIS先有些了解,但理解不深的,可试着带着这句话去看文档 atm3...

    java开源包1

    GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet....

Global site tag (gtag.js) - Google Analytics