`
jindw
  • 浏览: 514097 次
  • 性别: 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  
函数定义是在变量赋值之前完成的,所以函数定义会被变量赋值覆盖,而变量赋值不会被函数定义覆盖。将匿名函数赋值给变量是一个赋值的过程,所以这个会覆盖前面赋值的变量。道理就是这么简单的。

相关推荐

    H.rar_H-fox_H函数_fox H 函数_fox-H函数_双变量fox

    双变量Fox's H函数则是在单变量Fox's H函数基础上扩展而来的,用于处理两个独立变量(如时间和空间)的情况。在自由空间光通信中,这可能对应于时间上的光强波动和空间上的光束扩散。这种双变量特性使得H函数在分析...

    支持中文函数名中文变量名的delphi脚本语言

    然而,在传统的Pascal或Delphi语法中,函数名和变量名通常仅限于ASCII字符集,这在处理中文内容时可能会构成限制。随着全球化的发展,特别是对于中文开发者和用户来说,支持中文函数名和变量名的需求日益增长。 ...

    3.8 函数参数与变量的作用域(ppt).pdf

    变量分为局部变量和全局变量。局部变量是在特定函数或过程中定义的,它们仅限于该函数或过程内部使用,外部无法直接访问。这意味着每个函数可以有自己的独立变量,互不影响,有利于数据的安全和管理。例如,在函数`...

    高斯随机变量的特征函数

    高斯随机变量是服从正态分布的随机变量,其概率密度函数具有对称性和钟形特性,中心峰值位于均值处,方差决定了分布的宽度。 正态分布的概率密度函数(PDF)可以表示为: \[ f(x|\mu,\sigma^2) = \frac{1}{\sqrt{2\...

    如何在对话框类内调用视图cview类中的函数和变量

    "如何在对话框类内调用视图CView类中的函数和变量" 在SDI程序中,经常需要在对话框类中调用视图CView类中的函数和变量。下面将详细介绍如何实现这一点。 首先,需要了解SDI程序的结构。SDI程序中有三个主要类:...

    随机数个相互独立的随机变量之和的分布函数

    在概率论和统计学中,随机变量的分布函数是用来描述随机变量取值的概率规律的函数。当我们面对多个相互独立的随机变量时,了解它们之和的分布函数就变得尤为重要,因为这有助于我们理解和分析这些随机变量共同作用的...

    C# lua库 支持中文函数名中文变量

    本文将深入探讨“C# lua库 支持中文函数名中文变量”这一主题,基于lua 5.1版本,我们将讨论如何在C#中集成Lua,并支持中文的函数名和变量名。 首先,Lua是一种轻量级的脚本语言,以其简洁的语法和高效的执行性能而...

    linux常用c函数 环境变量篇.txt

    ### Linux C函数环境变量...这些函数在编写Linux下的C程序时非常有用,可以帮助我们更灵活地管理和利用环境变量。在实际编程过程中,应当注意函数的正确调用顺序,并妥善处理错误情况,以确保程序的健壮性和安全性。

    LIN API共用函数库和变量定义

    针对日本瑞莎单片机r8c/23 开发的LIN网络通讯程序包括主节点和从节点,此文件为共用函数库和变量定义

    基于Copula联结函数的两变量洪水频率分析

    基于Copula联结函数的两变量洪水频率分析,吕艳军,,采用二维Archimedean Copula 函数中的Gumbel-Hougaard Copula函数和Clayton Copula函数来研究红水河流域某个站点的洪峰流量Q和15天洪水总量V15及洪峰�

    MATLAB自定义函数及局部变量.pdf

    在 MATLAB 中,变量可以分为局部变量和全局变量。局部变量是指在函数内部使用的变量,这些变量只在函数内部有效,一旦函数返回,这些变量将自动清除。全局变量是指在整个程序中都起作用的变量,可以使用 global 来...

    二维随机变量分布函数习题

    2. **联合分布函数和边缘分布函数**:联合分布函数F(x, y)给出了二维随机变量(X, Y)所有可能值的概率,而边缘分布函数F_X(x)和F_Y(y)分别表示X和Y的单独分布,可以通过对另一个变量积分得到,即F_X(x) = ∫F(x, y)dy...

    php变量缓存到文件的函数

    php变量缓存到文件的函数 SAVE函数有用哦。php变量缓存到文件的函数 SAVE函数有用哦。php变量缓存到文件的函数 SAVE函数有用哦。php变量缓存到文件的函数 SAVE函数有用哦。php变量缓存到文件的函数 SAVE函数有用哦。...

    vb的示例程序自定义函数、变量应用、单元格条件颜色等

    在VB(Visual Basic)编程环境中,自定义函数和变量的应用是构建高效代码的关键元素,而单元格条件颜色则涉及到用户界面的美化和数据可视化。在这个VB示例程序中,我们将深入探讨这些知识点。 首先,自定义函数是VB...

    利用subs函数对符号表达的变量进行替换求值

    例如,假设在 Matlab 工作空间中存在变量 a = 980 和 C2 = 3时,使用 subs 函数可以对符号表达式 y = C2/exp(a*t) 中的变量 a 进行替换求值。 y = dsolve('Dy = -a*y'); a = 980; C2 = 3; subs(y) 输出结果:ans =...

    MATLAB自定义函数及局部变量.docx

    4. **函数体**:这是实际执行的代码,其中的变量分为两种类型:局部变量和全局变量。所有未在`function`语句中明确声明的变量都是局部变量,仅在函数内部存在,当函数执行完毕后会被自动清除。如果需要在多个函数...

    基于Python函数和变量名解析

    函数是Python为了代码最大程度的重用和最小化代码冗余而提供的基本程序结构,用于将相关功能打包并参数化 Python中可以创建4种函数: 1)、全局函数:定义在模块中,每个模块都是一个全局作用域,因此,全局作用域...

    python 实现函数交换变量

    # 题目:两个变量值用函数互换。

    JavaScript闭包函数访问外部变量的方法

    为了更加清晰地理解闭包和匿名函数在访问外部变量时的区别,我们可以参考两个经典的例子。第一个例子是通过数组存储闭包,然后遍历数组执行闭包,观察闭包对变量的引用情况。第二个例子是通过绑定事件处理器的方式,...

    C语言从入门到精通视频教程下载第10章 函数中的变量.zip

    虽然全局变量提供了跨函数的数据共享,但过度依赖全局变量可能导致代码难以理解和维护,因此在编程实践中,一般推荐优先使用局部变量和函数参数。 函数参数也是一种传递数据的方式。在C语言中,参数传递主要有两种...

Global site tag (gtag.js) - Google Analytics