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

相关推荐

    408函数-递归调用-全局变量和局部变量

    ### 408函数-递归调用-全局变量和局部变量 #### 一、CLion环境下新建文件及函数管理 在CLion环境下新建文件,尤其是C/C++源文件,对于项目的组织和代码的管理非常关键。为了提高编程效率和调试效果,我们需要遵循...

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

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

    matlab构造含符号变量的函数并求值

    在MATLAB中,符号变量和符号函数的使用是数学建模和符号计算的重要部分。本文将详细介绍如何在MATLAB中构造含符号变量的函数并进行求值。 首先,我们需要了解什么是符号变量。在MATLAB中,符号变量允许我们进行符号...

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

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

    python类和函数中使用静态变量的方法

    在Python编程语言中,类和函数的静态变量通常是指那些在多次调用中保持其值不变的变量。静态变量在其他面向对象的语言如Java或C#中比较常见,但在Python中,由于其动态特性的存在,实现静态变量的方式稍有不同。本篇...

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

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

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

    在MATLAB中,变量可以分为局部变量和全局变量。如果一个函数内的变量没有特别声明,那么这个变量只在函数内部使用,即为局部变量。如果两个或多个函数共用一个变量(或者说在子程序中也要用到主程序中的变量,注意...

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

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

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

    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函数有用哦。...

    authorware变量、函数大全

    用法 numlist=[1,2,3] AddLinear(numList,99,1) NumList为[99,1,2,3] numList:=[1,2,3] Addlinear(numList,99,6) NumList为[1,2,3,0,0,99] 相关函数 参见系统函数SortByProperty和SortByValue。

    函数自变量取值范围专题练习.doc

    这些知识点涵盖了函数自变量取值范围的基本原则和方法。在解决实际问题时,我们需要根据函数的具体形式,结合这些原则来确定自变量的允许取值。通过这些练习题,我们可以加深对函数定义域的理解,并能熟练地求解复杂...

    基于Python函数和变量名解析

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

    复杂函数申明解析

    在C或C++编程语言中,函数声明的解析尤其是复杂声明的解析对于初学者和经验丰富的程序员都可能是个挑战。本文将带领读者逐步解析C/C++中的复杂函数声明,包括使用typedef、const关键字以及函数指针的高级用法,并...

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

    在编程语言中,函数参数与变量的作用域是至关重要的概念,它们直接影响到代码的可读性、维护性和正确性。作用域定义了变量在程序中的可见范围,而函数参数则是函数接收输入数据的方式。让我们深入探讨这两个主题。 ...

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

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

    Perl常用函数&特殊变量

    Perl常用函数&特殊变量,便于查阅

    人教版八年级下册数学教案:19.1.1变量与函数第一课时变量与常量.docx

    本节课的主要目标是让学生理解变量和常量的概念,并学习如何用一个变量表示出另一个变量。通过本节课的学习,学生将能够说出变量和常量的概念,并能够自己用一个变量表示出另一个变量。 变量是指在一个变化过程中,...

    在MFC下如何定义全局变量和全局函数

    在MFC(Microsoft Foundation Classes)框架下开发应用时,全局变量和全局函数的定义与普通C++项目有所不同,因为MFC项目的结构更为复杂,通常由多个文件构成,直接在类外部定义全局变量可能会导致编译错误或者链接...

    基于预测函数与线性多变量反馈控制的同步发电机励磁控制

    将2种励磁控制器进行结合,以实现预测函数和线性多变量并行励磁控制,在有效提高电力系统稳定性的前提下还能解决发电机端电压的稳定问题。最后通过MATLAB/Simulink仿真证明了改进后的励磁控制器的有效性。

Global site tag (gtag.js) - Google Analytics