函数作用域和块作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。
但是,作用域同其中的变量声明出现的位置有某种微秒的联系。
直觉上认为,js代码在执行时是由上到下一行一行执行的,但是实际上并不完全正确。典型代码如下所示:
a=2;
var a;
console.log(a);//2
这段代码的运行结果是2,不符合由上到下一行一行执行的预期!
我们再看另外一段代码:
console.log(a);//undefined
var a=2;
这次,输出的结果是 undefined!也不符合由上到下一行一行的预期,借鉴上一段代码,有猜测认为它输出的是2,但是都不是!
到底发生了什么?是声明(蛋)在前还是赋值(鸡)在前?
js引擎会在解释js代码之前首先对其进行编译,编译阶段中的一部分工作就是找到所有声明,并用合适的作用域将它们关联起来。
包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
当遇到 var a=2; 时,但是js实际上会将其看成两个声明:var a; a=2;
前一个声明是在编译阶段进行的,第二个赋值声明会被留在原地等待执行阶段。
所以,本文第一段代码的等效代码如下:
var a;
a=2;
console.log(a);
第二段代码的等效代码如下:
var a;
console.log(a);
a=2;
这个过程,就像 变量和函数声明从它们在代码中出现的位置被“移动”到了最上面,这个过程叫做提升!
先有蛋(声明),后有鸡(赋值)。
只有声明会提升!!而不是赋值语句被提升。
函数声明也会被提升,代码如下所示:
foo();
function foo(){
console.log("hello");
}
foo函数的声明被提升了,因此调用语句可以正常执行!
函数声明会提升,但是函数表达式却不会提升!
我们看一下下面的代码:
console.log(foo);//undefined
foo();//type error foo is not a function
var foo=function bar(){
console.log("hello");
}
这段foo的声明会被提升并分配给所在作用域,因此调用foo不会reference error而是typeError,直接输出会报undefined;
跟本文第二段代码效果一样,声明虽然被提升,但是赋值并未被提升。
即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用:
foo();//Type error
bar();//Reference error
var foo=function bar(){
console.log("hello");
}
这部分代码经过提升后,实际上会被理解成如下形式:
var foo;
foo();//Type error
bar();//Reference error
foo=function (){
var bar=...self...
console.log("hello");
}
函数声明和变量声明都会被提升,但是函数声明会首先被提升,然后才是变量!
foo();
var foo;
function foo(){
console.log("1");
}
foo=function(){
console.log("2");
}
这段代码会输出1而不是2,这段代码会被引擎理解成如下形式:
function foo(){
console.log("1");
}
foo();
foo=function(){
console.log("2");
}
注意!var foo 尽管出现在函数声明 function foo()的声明之前,但是他是重复的声明,因此被忽略了,因为函数声明会被提升到普通变量之前!
重复的var声明会被忽略,但是出现在后面的函数声明可以覆盖前面的!
foo();//2
function foo(){
console.log("1");
}
function foo(){
console.log("2");
}