`
jindw
  • 浏览: 501423 次
  • 性别: Icon_minigender_1
  • 来自: 初到北京
社区版块
存档分类
最新评论

函数申明和变量申明的微妙区别

阅读更多
<script>
var test1 = 1;
function test1(){};//函数申明不能覆盖变量申明?
alert(test1);
</script>
<script>
var test2 = 1;
eval('function test2(){};');
alert(test2);//函数申明怎么又覆盖了变量申明?
</script>
分享到:
评论
48 楼 andot 2007-05-22  
这个其实很简单,看看这篇文章就知道了:http://www.coolcode.cn/andot/javascript-oop-inheritance-polymorphism/255
47 楼 379548695 2007-05-21  
dlee的解释收获不少啊!
46 楼 jianfeng008cn 2007-05-21  
很需要这样的牛在关键点上给我们解解惑啊!
45 楼 hax 2007-05-21  
jindw 写道
hax 写道
dlee 写道
是的,这就是上面我最后补充的一句话的情况:
引用
当然,如果在外部的代码中包含有函数调用,其调用的这个函数是会在调用之前创建的。



你之前的描述是错误的(就ecma规范而言,实际引擎怎么实现,偶们都不清楚)。
根据ecma-262规范edition 3,函数声明(注意var x = function () {}是函数表达式,不是函数声明)会被放到函数体或者代码块的最前端。所以不管外部代码有没有函数调用,都是先创建函数声明所声明的函数(靠,绕口令了)。



我咋没找到呢,记得在那里吗?
我去学习学习。



忘记了。整个ecma规范就是诘屈聱牙,看的人很不爽。找不到也是非常正常的。。。。

我估计在什么execution context之类的章节里。。。
44 楼 hax 2007-05-21  
andot 写道
这两种有差别,

var a = funciton() {}; 是定义一个变量a,定义一个匿名函数,然后将匿名函数赋值给变量a。

而 function a(){} 只是声明一个名字为a的函数。

function a(){} 比 var a = funciton() {}; 执行效率高一点点。但是 var a = funciton() {}; 这种方式更灵活一些。



到目前为之,没有任何理论理由或者实际的测试表明函数声明比函数表达式效率高“一点点”。
43 楼 jindw 2007-05-21  
hax 写道
dlee 写道
是的,这就是上面我最后补充的一句话的情况:
引用
当然,如果在外部的代码中包含有函数调用,其调用的这个函数是会在调用之前创建的。



你之前的描述是错误的(就ecma规范而言,实际引擎怎么实现,偶们都不清楚)。
根据ecma-262规范edition 3,函数声明(注意var x = function () {}是函数表达式,不是函数声明)会被放到函数体或者代码块的最前端。所以不管外部代码有没有函数调用,都是先创建函数声明所声明的函数(靠,绕口令了)。



我咋没找到呢,记得在那里吗?
我去学习学习。
42 楼 hax 2007-05-21  
dlee 写道
可能就是因为这个原因,在很多的Ajax组件库中都采用了使用匿名函数赋值的方法,因为这样做不容易被覆盖。

另外像Dojo这样的库都将全部的函数都放在某个名字空间变量中,例如:
dojo.byId或者dojo.io.bind,
虽然有很多Prototype Fans认为$函数才是最方便优雅的写法,但是dojo.byId用起来会更安全一些,因为开发者定义一个变量名称为dojo.byId是不可能的(变量名中不能出现“.”)。


dojo.byId为什么更安全?其实也不安全,因为dojo这个变量也会被覆盖。只是概率比$小一些罢了。
根本上的解决需要jsi或pies这样的框架。
41 楼 jindw 2007-05-21  
晕,该贴的第四页几乎被hax一头牛给包了^_^
40 楼 hax 2007-05-21  
dlee 写道
to Readonly:
与执行顺序无关。
症状是:只要创建了一个名称为xxx的普通变量,再以最初一楼代码中的那种方式创建名称为xxx的函数就是无效的(并没有创建这个函数),无论将这个函数声明放在哪里。只能通过用匿名函数明确地为xxx变量赋值,才可能绕过这个问题。

这个症状在IE和Firefox中是一致的。


解释就是他们都遵循ecma规范,把函数声明置前,所以被后面的变量声明覆盖了。而匿名函数不是函数声明而是函数表达式,只在执行时才会创建该function对象。
39 楼 hax 2007-05-21  
dlee 写道
呵呵,严格说来,其实现在所有现代高级编程语言都有一个编译的步骤,几乎已经不存在纯粹的解释型语言了。
所以很多未执行的函数中存在代码错误,JavaScript引擎都能够及早报告。如果是解释型语言,是不会有这种行为的。

考虑到JavaScript存在的编译步骤,最初提到的JavaScript代码的这种行为的解释就变得很合理了,因为这些代码并不是逐句解释执行的。



但是这个步骤并不一定是“编译”,至少ecma规范管这个步骤叫 verify (记忆中)。


我个人认为,你只能说它不是一个传统的纯解释性的语言,但也不能说js是编译语言呀。
38 楼 hax 2007-05-21  
dlee 写道
to sp42:
你的这个解释也有一些问题。另外由于JavaScript引擎创建声明的函数采用的是异步的方式,有些错误你使用调试器未必能跟踪到。因为你进入到调试器中以后,函数肯定是已经创建好了。

我再给出一种可能的解释。很多人认为JavaScript是一种解释型的语言,其实JavaScript完全是一种编译型的语言,它每次都是要将代码先编译到内存后然后才执行的。我估计它是将所有的外部代码也放在一个匿名函数之中来编译的,然后立即执行这个匿名函数。而其他的函数,除了在匿名函数中调用到(直接或间接)的以外,都是放在匿名函数执行完之后才以异步的方式来创建的。

以这种方式来实现,是一种效率较高的实现方式,因为编译或eval是一个相当慢的操作。



不知道您老是怎么得出函数声明是异步的这个结论。至少从ecma-262e3规范和jscript的表现,我无法看出这点。相反,ecma规范是要求函数声明被预先处理的。

关于你说的js的编译问题,我认为你有点夸大了,解释和编译没有很大的分界线,只要保证语义不变,行为一致就可以了。但是js的编译至多类似于java的字节码,甚至连jit也达不到(少数特例js引擎除外),所以你这样说编译是误导的。至于你估计的那个异步过程,我认为是纯粹想像,没有任何证据,而jscript调试器给出的反例你的解释十分无力。
37 楼 hax 2007-05-21  
jindw 写道
在rhino实现里面,函数的变量申明和内部函数申明是分开来管理的。
应该有一个先后,但是初始化函数的位置,又还是原来的位置。

初试化变量的时候,正如dlee所说,创建了一个空函数。

to sp42 你那个工具还没用过呢,看起来不错呢:)
在什么地方的?



没错,这就是spec没说的那部分。js2.0似乎会明确的确认这一点。

这个问题最影响的一点是当时function的scope到底是什么。例如包裹在with之内的function声明,到底是否受到with影响,ff引擎、jscript 5.5+、opera(我只验证过9.0)都是受到的。safari没有测试过。但是jscript 5.0就不受影响。我的pies的内部import特性就依赖于这个标准没有规定的危险特性,呵呵,所以pies在jscript 5的话,就只能用外部import,不能可靠的使用内部import。

sp42的工具就是ms script editor,office或者visual stuidio会带。可以给jscript断点,用来探索jscript的各种bug是不错的,呵呵。
36 楼 hax 2007-05-21  
dlee 写道
以前有个人曾经问过我,我在IE和Firefox上试过,行为都是这样的。

我猜想应该是考虑到执行效率的原因。这样做将创建声明函数(通过eval类似的机制)的工作放在最后,这部分工作可以以异步的方式来做,而不会影响到页面的render,而外部的代码在统一eval之后也可以尽快执行。

所以做JavaScript开发,不应该对函数创建的时机作出假设。而且函数创建的行为可能是异步的,执行到函数声明之后的这条语句时,函数尚未创建是完全有可能的。



同上,你的论断是错误的。

函数声明不管在哪里,都等价于放在该代码块或函数体的最前端。这里不存在异步的问题。而函数表达式则是另一回事情,可以认为是执行到该语句才创建的。

应该说这样一种设计是不太直观的,令人有些混淆,但是目前的现实就是如此。

这里存在两个例外,第一个是eval,eval中如果有函数声明,也是到eval执行的时候才起作用。但是eval内部仍旧是遵循我说的这个规则的。例如 eval('test1(); function test1(){...}'); 这个代码是ok的。另外一个是,按照标准,function声明是不能在statement里的,也就是说如下代码都是错误的:
if (x > 1) {
  function test() {...}
}

with (x) {
  function test() {...}
}


但是ie和moz都接受这个代码(有少数引擎不行,似乎safari就不行),其处理方式却可能不相同,这是spec以外的部分,所以也无话可说。
35 楼 hax 2007-05-21  
jindw 写道
没找到合适的解释


你没有找仔细啊,呵呵。
34 楼 hax 2007-05-21  
dlee 写道
是的,这就是上面我最后补充的一句话的情况:
引用
当然,如果在外部的代码中包含有函数调用,其调用的这个函数是会在调用之前创建的。



你之前的描述是错误的(就ecma规范而言,实际引擎怎么实现,偶们都不清楚)。
根据ecma-262规范edition 3,函数声明(注意var x = function () {}是函数表达式,不是函数声明)会被放到函数体或者代码块的最前端。所以不管外部代码有没有函数调用,都是先创建函数声明所声明的函数(靠,绕口令了)。
33 楼 netfishx 2007-05-16  
引用
netfishx(151431) 14:45:28
哦,这个我也早就想过
netfishx(151431) 14:45:47
某个老外大牛就很坚定的说,js是有预编译的
netfishx(151431) 14:46:02
但在不同的环境下,顺序又有所不同
32 楼 andot 2007-05-10  
这两种有差别,

var a = funciton() {}; 是定义一个变量a,定义一个匿名函数,然后将匿名函数赋值给变量a。

而 function a(){} 只是声明一个名字为a的函数。

function a(){} 比 var a = funciton() {}; 执行效率高一点点。但是 var a = funciton() {}; 这种方式更灵活一些。
31 楼 i_love_sc 2007-05-10  
<div class='code_title'>js 代码</div>
<div class='dp-highlighter'>
<div class='bar'> </div>
<ol class='dp-c' start='1'>
    <li class='alt'><span><span class='keyword'>var</span><span> a = </span><span class='keyword'>function</span><span>() {  </span></span></li>
    <li class=''><span>};  </span></li>
    <li class='alt'><span>  </span></li>
    <li class=''><span><span class='keyword'>function</span><span> a() {  </span></span></li>
    <li class='alt'><span>}  </span></li>
</ol>
</div>
<br/>
这两种申明在效率上有差别吗 ?<br/>
我已经习惯第一种了,因为在我看来,function也是一种基本类型。
30 楼 andot 2007-05-10  
另外,变量定义和变量赋值也是两个过程。var a; 是变量定义,a=1; 是变量赋值,var a=1; 是变量定义和变量赋值。虽然你写在一起,但是这个过程在js解析器解析的过程中也是分两步完成的,变量定义和函数定义是首先完成的,接下来执行的才是变量赋值。
29 楼 andot 2007-05-10  
函数定义是在变量赋值之前完成的,所以函数定义会被变量赋值覆盖,而变量赋值不会被函数定义覆盖。将匿名函数赋值给变量是一个赋值的过程,所以这个会覆盖前面赋值的变量。道理就是这么简单的。

相关推荐

Global site tag (gtag.js) - Google Analytics