js基础阵营:变量篇之变量,函数提升

上面已经介绍完了变量的定义,以及变量的作用域问题,这章我们来看看变量提升的问题。在进行这章开始前,我们先来探讨一个很悠久的问题,那就是
image.png

哈哈。开玩笑。言归正传。我们先看下面代码

console.log(a);
var  a = 2;

输出结果是
image.png

直觉上我们理解的是程序会从上往下,顺序执行,按照这个逻辑,我们应该得到的是这样的
image.png

但是实际上感觉是代码是这样的
var a;
console.log(a);
a = 2;

为什么会这样呢?这里就需要知道编辑器在执行程序所需要的做的事了,编辑器在执行程序的时候会先执行编译,那么编译阶段有一部分的工作是找到所在作用域中的所有的声明,所以我们可以理解为,在js中任何代码在执行前,其(函数,变量)声明都会被先处理。因此,我们看到var a = 2;其实在编译器中是var a和a = 2;两部分。var a,是在编译阶段执行的,a = 2在执行阶段执行。这个过程就好像变量以及函数声明从他们原有的代码处被移动了,所以叫做提升。
上面说的是变量,那么函数呢?是否也会提升呢?我们来看下下面的例子

sayName();
function sayName(){
  console.log('来瓶二锅头');
}

将上面代码放到控制台,看下结果
image.png

显而易见,我们的函数声明也提升了,实际上在执行的时候代码是这样的

function sayName(){
  console.log('来瓶二锅头');
}
sayName();

那么函数内部的变量是否也会提升呢?我们再来看下这个例子

function sayName(){
  console.log(name);
  var name = '来瓶二锅头';
}
sayName();

在控制台我们看下结果
image.png

可以看到我们输出的是undefined,而不是referenceError.所以函数中的变量也得到了提升,实际的代码是这样的

function sayName(){
  var name;
  console.log(name);
  name = '来瓶二锅头';
}
sayName();

所以前面我理解的结论 在js中任何代码在执行前,其(函数,变量)声明都会被先处理是正确的。
此时我们在针对函数来想下,除了上面的函数声明,我们如果通过函数表达式是否会得到提升呢?看下下面的代码

sayName();
var sayName = function (){
  console.log('来瓶二锅头');
}

输出到控制台,看下结果
image.png

此时报错不在referenceError而是TypeError。那么我们在试下下面的代码看看

console.log(sayName);
var sayName = function (){
  console.log('来瓶二锅头');
}

在控制台执行下看看结果
image.png

至此大家应该明白啥意思了吧。前面说过了,我们会把声明提前,但是执行的依旧在原位置,所以程序执行前面的代码的时候,实际上是这样的

var sayName;
sayName();
sayName = function (){
  console.log('来瓶二锅头');
}

这个时候,sayName只是被定义了,不是一个函数,所以执行sayName()会报TypeError,而执行console.log(sayName);也不会报错
那么我们如果通过函数表达式加具名函数一起使用,会是什么样子呢?一起看下下面代码

sayName();
a();
var sayName = function a(){
  console.log('来瓶二锅头')
}

在控制台执行下,看下结果
image.png

可以看到sayName是报TypeError,但是a()是报ReferenceError错误。sayName上面说过了,那a()是为什么呢?我们在看下下面的代码

var sayName = function a(){
  console.log(a);
  console.log('来瓶二锅头')
}
sayName();

控制台看下结果
image.png

那么我们可以看出实际上程序在执行的时候,代码是下面这样的

var sayName;
sayName = function(){
  var a = 函数本身;
  console.log(a);
  console.log('来瓶二锅头')
}
sayName();

所以我们从另一个角度也能得出一个结论,那就是如果函数表达式是跟着具名函数,那么具名函数实际是这个方法本身。在其函数作用域内生效。是不是很神奇。那么原理具体是什么呢?我们在后面的函数篇来给出解答(挖坑,看下后面是否真的可以给填上,哈哈)。

上面说到了函数以及变量提升的问题,那么我们再来思考一个问题,那就是如果函数名和变量名相同的时候,谁在前呢?
image.png

我们来看下下面的例子
test()
var test;
function test() {
  console.log('a');
}
test = function(){
  console.log('b');
}

控制台执行结果为
image.png

那么我们可以看出,在执行过程中,实际上为

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')
    }
}

结果为
image.png

为什么呢?我们都知道普通的块级作用域会将变量提升到上级作用域中,那么实际上代码逻辑为

var test1;
test1();
if(true){
  test1 = function(){
   console.log('1')
  }
}

所以你懂了吗?至此关于提升的问题结束了。接下来我们将看看变量的类型一系列问题。

你可能感兴趣的:(js基础阵营:变量篇之变量,函数提升)