JavaScript问题:var a=5; var a; alert( a ) // 5。请问这是为什么?



 var a=5;  var a;  alert( a )  // 5

我的理解:
第一次var a: var a=5; ==>在内存中开辟空间a,a中保存了数字5。
第二次var a :var a; ==>在内存中开辟空间a,a中什么都没有。

程序从上至下,从左至右执行,所以最后弹出的是 “第二次 var a” 的结果,第二次 var a中,a没有保存值,所以是undefined。

但执行结果是5, 请问一下alert为什么不是undefined?


当局部变量和函数参数同名时,该怎么理解呢?也和预解析有关系吗?


 function test(a){
        var a=a||5;
        alert(a)
}

test() //没传参的话,就是5;传参的话就alert参数

web前端开发 JavaScript

「一方通行」 8 years, 8 months ago

JavaScript有预解析行为,整段代码的运行过程为:

  1. 进行预解析声明, var a

  2. 代码逐步执行过程,此时 a = 5 ,而第二行代码 var a 在此处没有起到作用。

所以弹出的值为5,而不是undefined。

可以看看 解释JavaScript的“预解析(置顶解析)”

mzzaj answered 8 years, 8 months ago


 var a=5;  var a;  alert( a )  // 5

等同于


 var a;  a = 5;  alert( a )  // 5

js在预编译阶段,会忽略重复的声明,然后在运行阶段赋值;

zybwh answered 8 years, 8 months ago

JavaScript程序执行,先进行预解析,将 var 和 function 关键字开头的变量和函数会进行提升,而后逐行执行代码。在你的程序中,先将 var 声明的变量 a 进行提升,并对其赋值 undefined(进行了二次) ,然后逐行执行代码,先赋值为5,而后什么也没做,再alert出来。

更多细节,你可以参考我的之前写的博客 http://zhenghaoju700.blog.163.com/blog/static/135859518201441711516713/

更详细的JavaScript程序执行 可以参看这个 http://zhenghaoju700.blog.163.com/blog/static/13585951820144215281510/

如果逐行执行遇到函数执行时,会在进行一次预解析和逐行执行(函数有自己的作用域),形参可以理解成var a = undefined,当你传入实参时,相当于进行了赋值。函数内部情况和全局作用域下是一样的。

黑丝腹黑控 answered 8 years, 8 months ago

节奏是这样:


 声明第一个 a;
声明第二个 a,发现已经声明了,不重新声明,跳过;
赋值 a 等于5;
执行alert(a)

done~

caocao answered 8 years, 8 months ago

这个问题的答案其实叫做 变量声明提升(hoisting)

所有的变量声明,函数定义(指的 function fn() {} 这种写法),都会被提升到本作用域的最开始,但是赋值还是在原本的位置。所以上面那段代码其实相当于:


 var a;
//var a;
// hoisting
a = 5;
alert(5);

既然大家都点赞了,我就好好回答一下第二个问题吧
除去 IE 这个大bug 不说 http://hax.iteye.com/blog/349569

ES5.1 里面函数是这样执行的(不讨论 use strict 和一些特殊情况,JS好复杂的),按如下顺序执行:

  1. 确定“this”的值 (确切的来说, this 在JS里面不是一个变量名而是一个关键字)

  2. 创建一个新的作用域

  3. 处理形参/实参(没有定义过才声明,无论如何都重新赋值,没有对应实参则赋值为"undefined"):
    对于每一个传入的实参,按照从左往右的顺序依次执行:如果对应的形参在本作用域中还没有定义,则在本作用域中声明形参,并赋值。如果已经定义过了,则重新给其赋值。(没有对应实参则赋值为"undefined")(没有定义:就是“没有声明”的意思)

  4. 处理函数定义(没有定义过才声明,无论如何都重新赋值):
    对该函数中所有的定义的函数,按照代码写的顺序依次执行:如果这个变量名在本作用域中还没有定义,则在本作用域中声明这个函数名,并且赋值为对应的函数,如果定义了这个变量,在可写的情况下重新给这个变量赋值为这个函数,否则抛出异常。

  5. 处理 "arguments"(没有定义过才声明和赋值):
    如果在本作用域中没有定义 arguments ,则在本作用域中声明 arguments 并给其赋值。

  6. 处理变量声明(没有定义过才声明,不赋值):
    对于所有变量声明,按照代码写的顺序依次执行:如果在本作用域中没有定义这个变量,则在本作用域中声明这个变量,赋值为 undefined

  7. 然后执行函数代码。(当然是去变量定义里面的 var 执行)

参考: http://www.ecma-international.org/ecma-262/5.1/#sec-10.5

所以当在函数执行的时候,会自动除去所有的声明,访问一个变量(如果你的代码里没有重新给其赋值的话)查找的顺序其实是:最后一个定义的函数名?,最后一个形参?(e.g. 形参可以重名,以最右边为准)?, arguments ?,最前面声明的那个变量?

所以说,楼主第二问很明确了,如果 test 传入参数,就会赋给 a ,如果没有传入参数 a 就是 undefined ,然后除去声明执行代码就是 a=a||5; (此时访问的 a 是形参里声明的那个 a ,后面的声明自动忽略了)

穹丶love answered 8 years, 8 months ago

Your Answer