一、什么是变量声明提前
变量在声明它们的函数体以及这个函数体嵌套的任意函数体内始终可见。说直白点,在声明一个变量的前后,你都可以直接使用它,并不会报错。举个例子:
(function(){
console.log(a);//undefined
var a ="小钻风";
console.log(a);//小钻风
}())
前面已经说了,变量在声明它们的函数体内始终可见,尽管第一个console输出在声明a之前,但它依旧能输出,并不会报错,那是因为声明统一提前,赋值原地不变。上面代码等同于:
(function(){
var a;
console.log(a);//声明了但未赋值,所以输出undefined;
a ="小钻风";
console.log(a);//上一步赋值了,所以输出小钻风
}())
声明提前了,只是没有赋值,赋值仍保留远处不变,所以说变量a在function每一处都是可用的,就是这么个怪逻辑。
二、什么是函数声明提前(函数体提前)
函数声明提前的原理与变量声明提前情况类似,需要提醒的是,只有函数声明格式的函数才会存在函数声明提前,比如函数表达式,构造函数,都不存在函数声明提前。
函数创建的三种写法:
a.函数声明:function fun(a){console.log(a)};(只有这个家伙存在函数声明提前)
b.函数表达式:var fun = function(a){console.log(a)}(不提前);
c.构造函数:var fun = new Function(“a”,console.log(a))(不提前);
直接上个例子
num()//1
console.log(num)//函数本身
function num(){
console.log(1);
}
num();//1
console.log(num)//函数本身**
,函数声明的情况与变量声明类似,你可以理解为,在同一作用域内函数声明后,此函数会跑到本作用域的最前面。上面的代码等同于:
function num(){
console.log(1);
}
num()//1
console.log(num)//函数本身
num();//1
console.log(num)//函数本身**
那么再来看看函数表达式是否会函数体提前:
num()//报错
console.log(num)//undefined
var num = function (){
console.log(1);
}
num();//1
console.log(num)//函数本身
第一个num()就会报错,后面三个是看不到输出的,这里是假设不受num()报错影响本应输出的情况。为什么会这样呢,还记得前面变量声明提前的原理吗,这里只是将后面的普通赋值换成了函数,所以以上代码等同于:
var num;
num()//报错,这时候都没有函数声明
console.log(num)//undefined,因为已经声明了num
num = function (){
console.log(1);
}
num();//1,有函数了啊,可以调用了
console.log(num)//函数本身,有函数了。
三、变量声明提前,函数声明提前顺序
这里就有个问题了,函数声明提前,变量声明也提前,到底谁会更提的更前?假设两者都用的同一命名声明,到底最后会输出啥,我们来看个例子:
console.log(a);
var a = “孙悟空”;
function a(){ console.log(“小钻风”); }
照理说,函数先提前,然后变量a在提前,a未赋值,覆盖了上面声明的函数a,应该输出undefined,但为什么输出的还是函数本体?
引入一个概念,你不知道的JavaScript(上卷)一书的第40页中写到:函数会首先被提升,然后才是变量。也就是说,同一作用域下提前,函数会在更前面。以上代码等同于:
function a(){
console.log(“小钻风”);
}
var a;//由于上面函数已声明a,相同的变量名声明会被直接忽略
console.log(a);//输出函数本体
a = “孙悟空”;
为啥函数提前之后又var a;了怎么不输出undefined,因为这里只是再次声明a,并未修改现有a的值,做个简单测试就可以了:
var a=1;
var a;
console.log(a);//1
变量a已经声明过了,而且也赋值了,后面再次声明只是声明并未修改值,这种声明方式会被直接忽略,所以还是输出1.
下面是个例子
console.log(a)//?
a();//?
var a =3;
function a(){
console.log(10);
}
console.log(a);//?
a = 6;
a();//?
正确的修改
function a(){
console.log(10);
}
var a;//再次声明a,并未修改a的值,忽略此处声明
console.log(a)//输出函数本体
a();//函数声明提前,可调用,输出10
a =3;//这里修改值了,a=3,函数已不存在
console.log(a);//输出3
a = 6;//再次修改为6,函数已不存在
a();//a已经为6,没有函数所以没法调用,直接报错