哈哈。开玩笑。言归正传。我们先看下面代码
console.log(a);
var a = 2;
输出结果是
但是实际上感觉是代码是这样的
var a;
console.log(a);
a = 2;
为什么会这样呢?这里就需要知道编辑器在执行程序所需要的做的事了,编辑器在执行程序的时候会先执行编译,那么编译阶段有一部分的工作是找到所在作用域中的所有的声明,所以我们可以理解为,在js中任何代码在执行前,其(函数,变量)声明都会被先处理。因此,我们看到var a = 2;其实在编译器中是var a和a = 2;两部分。var a,是在编译阶段执行的,a = 2在执行阶段执行。这个过程就好像变量以及函数声明从他们原有的代码处被移动了,所以叫做提升。
上面说的是变量,那么函数呢?是否也会提升呢?我们来看下下面的例子
sayName();
function sayName(){
console.log('来瓶二锅头');
}
将上面代码放到控制台,看下结果
显而易见,我们的函数声明也提升了,实际上在执行的时候代码是这样的
function sayName(){
console.log('来瓶二锅头');
}
sayName();
那么函数内部的变量是否也会提升呢?我们再来看下这个例子
function sayName(){
console.log(name);
var name = '来瓶二锅头';
}
sayName();
在控制台我们看下结果
可以看到我们输出的是undefined,而不是referenceError.所以函数中的变量也得到了提升,实际的代码是这样的
function sayName(){
var name;
console.log(name);
name = '来瓶二锅头';
}
sayName();
所以前面我理解的结论 在js中任何代码在执行前,其(函数,变量)声明都会被先处理是正确的。
此时我们在针对函数来想下,除了上面的函数声明,我们如果通过函数表达式是否会得到提升呢?看下下面的代码
sayName();
var sayName = function (){
console.log('来瓶二锅头');
}
输出到控制台,看下结果
此时报错不在referenceError而是TypeError。那么我们在试下下面的代码看看
console.log(sayName);
var sayName = function (){
console.log('来瓶二锅头');
}
在控制台执行下看看结果
至此大家应该明白啥意思了吧。前面说过了,我们会把声明提前,但是执行的依旧在原位置,所以程序执行前面的代码的时候,实际上是这样的
var sayName;
sayName();
sayName = function (){
console.log('来瓶二锅头');
}
这个时候,sayName只是被定义了,不是一个函数,所以执行sayName()会报TypeError,而执行console.log(sayName);也不会报错
那么我们如果通过函数表达式加具名函数一起使用,会是什么样子呢?一起看下下面代码
sayName();
a();
var sayName = function a(){
console.log('来瓶二锅头')
}
在控制台执行下,看下结果
可以看到sayName是报TypeError,但是a()是报ReferenceError错误。sayName上面说过了,那a()是为什么呢?我们在看下下面的代码
var sayName = function a(){
console.log(a);
console.log('来瓶二锅头')
}
sayName();
控制台看下结果
那么我们可以看出实际上程序在执行的时候,代码是下面这样的
var sayName;
sayName = function(){
var a = 函数本身;
console.log(a);
console.log('来瓶二锅头')
}
sayName();
所以我们从另一个角度也能得出一个结论,那就是如果函数表达式是跟着具名函数,那么具名函数实际是这个方法本身。在其函数作用域内生效。是不是很神奇。那么原理具体是什么呢?我们在后面的函数篇来给出解答(挖坑,看下后面是否真的可以给填上,哈哈)。
我们来看下下面的例子
test()
var test;
function test() {
console.log('a');
}
test = function(){
console.log('b');
}
控制台执行结果为
那么我们可以看出,在执行过程中,实际上为
function test() {
console.log('a');
}
var test;
test();
test = function(){
console.log('b');
}
所以我们可以得出结论,在变量以及函数相同的情况下函数优先。我们再来思考一个问题,那就是变量实际上存在覆盖的问题,那么下面的代码执行的结果是啥呢?
test();
function test(){
console.log('a');
}
var test = function(){
console.log('b');
}
function test(){
console.log('c');
}
按照我们上面说的编译时候提升的问题,代码可以理解为
function test(){
console.log('a');
}
function test(){
console.log('c');
}
var test;
test();
test = function(){
console.log('b');
}
由于存在 重载,所以执行结果为c。
我们再来设想一个场景,如果我们在普通的块级作用域中定义函数,会怎么样呢?看下下面的代码
test1()
if(true){
function test1(){
console.log('1')
}
}
结果为
为什么呢?我们都知道普通的块级作用域会将变量提升到上级作用域中,那么实际上代码逻辑为
var test1;
test1();
if(true){
test1 = function(){
console.log('1')
}
}
所以你懂了吗?至此关于提升的问题结束了。接下来我们将看看变量的类型一系列问题。