或许你在编写JavaScript时,会注意到这么一个现象,声明的出现与其作用域内的操作位置有微妙的关系,
有人认为JavaScript引擎执行代码是从上到下一行一行执行的。实际上这不全对
例如:
a=2;
var a;
console.log(a);
如果按照JavaScript引擎是一行一行代码解释的话,先赋值再声明再输出,因为var a在a=2后面,理所当然认为a被重新赋值了,那么输出结果是undefined,但是很遗憾并不是,输出结果为2。
我们将它们之间的位置调换下会得到:
console.log(a);
var a;
a=2;
如果按照对之前代码进行的分析,那么此处会输出 "2" 或者这样思考,a未定义前就使用,那么此处会输出"ReferenceError"。 但是结果却是 "undefined" ,为什么???
这其实就是“先有鸡还是先有蛋的问题”即先声明在使用还是先使用在声明的问题
1.2编译器
编译器在JavaScript中的作用是进行词法分析/语法分析以及生成代码。编译的一部分工作----在词法分析中产生词法域,这时候会得到词法域以及各类声明。
包括函数和变量在内的所有声明在任何代码被执行之前首先被处理
当看到"var a=2"时你以为这会是一句声明语句,其实不然,这在JavaScript看来是两种声明,一种是声明语句"var a;",另一种是赋值语句"a=2;",声明语句在编译时就已经完成,而赋值语句则要在原地等待执行。
之前我们的代码它的执行过程如下
//先编译再执行
a=2; //等待执行
var a; //编译时先执行此声明
console.log(a);
如上所示,因为编译器首先是编译此代码随后再执行,所以先执行 var a ;在执行 a=2 ;最后执行 console.log(a);所以输出为2。
如果按照从上到下的执行顺序,那么上面代码可变为
var a; //先执行声明语句
a=2;
console.log(a);
那么,之前讲的这个代码片段
console.log(a);
var a=2;
可变为
var a;
console.log(a);
a=2;
在这里JavaScript将 var a=2 分解为 var a 和 a=2。var a 首先被执行,所以上升到第一位。
--------------------------------在上面例子中,var a 声明语句总是被提升到第一位,所以叫提升
提升过程有两点需要注意的
①函数声明也是可以提升的,
例如
foo(); //foo()函数能够正常运行
function foo(){ //函数声明发生了提升
console.log(a); //ReferenceError
var a=2;
}
函数声明提升,所以第一行调用可以正常执行。
②提升只会提升声明,而不会提升逻辑(赋值等操作)
如上面的 var a 被提升(是在foo(...)函数中的最上方,而不是整个程序),剩下 a=2 在原地。
这时候我们稍加对上面程序进行修改,你就会发现问题
foo(); //TypeError而不是ReferenceError
var foo=function bar(){ //函数声明发生了提升
console.log(a); //ReferenceError
var a=2;
}
var foo=function bar(){...} 中同样被分成两部分 var foo 以及 foo=function bar(){....},声明被提升到最顶部,赋值操作没有提升。
因此foo()调用没有发生ReferenceError错误。但是foo()并没有被赋值(如果它是一个函数声明而不是一个函数表达式,那么它就必须要赋值,此处它没有赋值,所以默认的值为undefined),foo()由于对undefined进行函数调用而导致非法操作,抛出TypeError。
1.3函数优先
函数声明和变量声明假设都在同一个作用域中,那么是函数优先还是变量声明优先呢??
我们看一个例子
foo(); //1 证明是函数声明优先
var foo;
function foo(){
console.log(1);
}
foo=function () {
console.log(2);
}
在以上代码中,我们可以看到,输出结果为1,从代码执行上来看,如果是变量声明优先的话,那么foo()会输出2,如果是函数优先那么会输出1。
这个代码可以化成这样的形式:
function foo(){
console.log(1);
};
foo();
foo=function(){
console.log(2);
};
注意,var foo 尽管出现在 function foo(...)声明之前,但是它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。---------------------这告诉我们,在一个作用域中重复定义是糟糕的,它可能会出现各种各样的问题!!!!