`
eclipse07
  • 浏览: 21144 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

Javascript的"预编译"思考

阅读更多
今天工作需要,搜索下JS面试题,看到一个题目,大约是这样的
<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 函数修改一下,题目变为
<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> 块。且看下面的代码
<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。

那么,将上面的题目再变换一下,如下
<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)。因此执行结果便如上面所示。

将题目再变化下,如下
<script>
   alert(typeof addB);
   addB();
   var addB = function addB() {
     alert("B executed!");
   };
</script>

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

那么,请看下面的代码
<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",上面的代码即使变为
<script>
   alert(typeof addB);
   function addB() {
       alert("function addB");
   }
   var addB = "variable";
   alert(addB);
</script>

结果也一样,这说明JS解析器先预声明变量,再预定义函数。

小结一下:JS 在执行前会进行类似"预编译"的操作,而且先预定义变量再预定义函数。
15
2
分享到:
评论
13 楼 FDI 2015-03-24  
请教下楼主,如果在不同代码块中存在同名的函数声明,在预编译期间后面的不会覆盖前面的吗?
12 楼 liu_87663663 2012-12-31  
讨论的很深刻,不错。
11 楼 finaland 2009-10-29  
..............你回复的也太快了吧,我正在一边思想斗争一边贴的时候,你居然已经回复了....
10 楼 finaland 2009-10-29  
[再接刚才的回复]

抱歉,刚才的回复贴了之后,总感觉针芒在背,坐立不安,根据经验,一定是我的回复中出现了bug 和疏漏,仔细考虑之后,应该是我混淆了变量的声明和赋值,于是测试了以下代码

<script>
    alert(typeof addB);
   
    if(addB == undefined){
      alert("addB is undefined");
    }
    var addB;
</script>

得到的结果是 undefined 和 addB is undefined

如果 addB 变量未申明
<script>
    alert(typeof addB);
   
    if(addB == undefined){
      alert("addB is undefined");
    }
</script>
得到的结果是 undefined 和 ie 报错:

---------------------------
Error
---------------------------
A Runtime Error has occurred.
Do you wish to Debug?

Line: 11
Error: 'addB' is undefined
---------------------------
Yes   No  
---------------------------

看来 var addB; 这句并不是可有可无的,"变量预处理过程" 这条喷火的龙烧焦了我的头发。
当然,用语可能需要规范一下,变量预申明,可能更确切一点
向LZ 和 参与讨论且认真看了前两贴的各位同学表示歉意
9 楼 finaland 2009-10-29  
[接刚才的回复]

理由是:

# <script> 
#    alert(typeof addB); 
# </script>
得到的结果是 undefiend

# <script> 
#    alert(typeof addB); 
#    var addB = "variable"; 
# </script>

得到的结果是也是 undefined

假设真的存在这么一个没有代码、没有调用、没有异常、对执行效果没有任何影响的“变量预处理过程”,那么根据奥卡姆剃刀原理,它只是车库里另一只喷火的龙罢了,我倾向于它不存在
8 楼 eclipse07 2009-10-29  
finaland 写道
最后的理解:“JS解析器先预定义变量,再预定义函数” 是错误的。
实际上 JS 解析器对于使用 function 定义的函数,确实有预处理,但是对于使用var定义的变量,是不进行预处理的,只是在代码运行时顺序执行。


确实说错了,应该是"先预声明变量,再预定义函数",变量是会进行预声明,但是赋值是在代码运行时执行。文中已修正。
7 楼 finaland 2009-10-29  
最后的理解:“JS解析器先预定义变量,再预定义函数” 是错误的。
实际上 JS 解析器对于使用 function 定义的函数,确实有预处理,但是对于使用var定义的变量,是不进行预处理的,只是在代码运行时顺序执行。
6 楼 tianshiyeben 2009-10-26  
楼主好认真  学习
5 楼 energykey 2009-10-26  
学习楼主分析问题的思路和探索精神。
4 楼 eclipse07 2009-10-23  
楼上的,当没有函数名时,这当然是一个函数表达式,如
    var addB = function() {};

但是,有函数名时,如
    var addB = function f() {};

这时 IE 认为 f 是一个函数声明。
3 楼 myali88 2009-10-23  
引用
IE 中将它也认为是一个函数声明是不合理的。
,如果是这样,IE对两段这addB的解释应该是一样的才对呀?
2 楼 eclipse07 2009-10-23  
myali88 写道
<script>
   alert(typeof addA);
   addA();
   function addA() {
      alert("A executed!");
   };
</script>
<script>
   alert(typeof addB);
   addB();
   var addB = function() {
     alert("B executed!");
   };
</script>
中在IE 6 下addB出现undefined,而下面这段同样IE 6 下却可以执行呢?单从script段来看,没什么不同啊!
<script>
   alert(typeof addB);
   addB();
   var addB = function addB() {
     alert("B executed!");
   };
</script>


可以这么说,
 var addB = function addB() {
     alert("B executed!");
 };

是一个赋值表达式,这儿的 function addB() 在赋值表达式中,因此它是一个函数表达式而非函数声明,就不应该进行预编译。 IE 中将它也认为是一个函数声明是不合理的。
1 楼 myali88 2009-10-23  
<script>
   alert(typeof addA);
   addA();
   function addA() {
      alert("A executed!");
   };
</script>
<script>
   alert(typeof addB);
   addB();
   var addB = function() {
     alert("B executed!");
   };
</script>
中在IE 6 下addB出现undefined,而下面这段同样IE 6 下却可以执行呢?单从script段来看,没什么不同啊!
<script>
   alert(typeof addB);
   addB();
   var addB = function addB() {
     alert("B executed!");
   };
</script>

相关推荐

Global site tag (gtag.js) - Google Analytics