JavaScript变量提升详解

变量提升

在js中我们用var声明的变量是存在变量提升的。换句话说,我们js的解释器在解释js代码的时候,会优先把所有的变量声明提到前面。

示例代码

var的变量提升

下面我演示的就是一个简单的变量提升的问题。

js源代码

console.log(a)
var a=1
console.log(a)

js解释器编译的顺序:

//js编译时会把所有的变量声明提前
var a;
console.log(a)//显示undefiend,因为此时a没有赋值不知道数据类型
a=1//赋值操作不提前,只是var a的声明提前了
console.log(a)//输出a

这里理解的要点是,js只是把变量的声明提前了。但是赋值并没有提前。var a=1其实是两行代码的简写,var a;a=1

function变量赋值提升

接下来这个问题可是经典的面试题

function foo(a){
    console.log(a)
    var a=2
    function a(){
        
    }
    console.log(a)
}
foo(10)

请问两次的console.log分别打印出什么呢?

想要搞清楚这个问题,我们需要理解变量提升的优先级。
其实在JS中,被变量提升的不仅仅有var还有我们的function,而且function提升变量的优先级要比var要高一些,而且这个函数变量提升还是变量和赋值同时提升!!!

所以上面这段代码在js解释器下是按照这样的顺序解释的

function foo(a){
    //function a变量提升,并赋值function
    var a=function (){
        
    }
    console.log(a)
    a=2
    console.log(a)
}
foo(10)

虽然我们调用foo函数时候传递了一个10进去,但是根据js的作用域链规则,js会优先在最小的作用域依次,向上、向外去寻找声明的变量。显然这个时候我在外面传的10就没有作用了,因为js在向上找的同时在{}里面找到了变量a的声明,也就是var a=function(),就没有必要向外找传递进来的10了

所以第一个打印语句打印的是函数本体,第二个打印的是2

理解上面代码的要点是理解function声明函数其实是变量和赋值同时提升,这里和我们的var是不一样的,var仅仅是声明提升,它的赋值不会提升,所以才有可能打印出undefined(undefined就是声明了但是却没有赋值的意思)

局部变量的偷偷赋值

我们看下下面案例的输出

function foo(a) {
    var a
    console.log(a)
}

foo(10)

输出结果是10

奇怪了这是为什么呢?a我们没有赋值呀,根据作用域链规则,应该是undefined呀。为什么传递进去的10,却打印出来了。

其实这里的底层是这样的

//在括号外部a接收了传递的10
function foo(var a=10) {
	//你自己重复声明变量
    var a
    console.log(a)
}

foo(10)

我们在console.log(a)时候,js沿着作用域链在括号内寻找,找到了a,发现是undefined,但是js不甘心,继续沿着作用域链向上检索,终于搜寻到外面的形参声明赋值,然后输出10
也就是说作用域链向上查找不是查到声明就完的,如果js发现它是undefined的话还会继续向上找

我们再来一道题练习一下作用域链

var a=10
var a=2
{
    var a
    console.log(a)
}

输出2,不是undifined,js找到undifined是不会甘心的,会继续向上找不是undifined的数值,找到了一个不是undifined就不继续找了

再来看一个有趣的事情

function foo(a){
    console.log(a)
    var a=2
    console.log(a)
}
foo(10)

这个我们来看js编译之后的执行顺序

//形参接收a=10 这里写var其实是有语法错误的,我这样写是为了让大家理解
function foo(var a=10){
    console.log(a)
    a=2
    console.log(a)
}
foo(10)

显然输出10和2

箭头函数和function不一样

function foo(a) {
    console.log(a)
    a = () => {

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

foo(10)

输出什么呢?

我们来分析一下js编译之后的执行顺序

//局部变量声明并接收参数赋值
function foo(var a=10) {
    var a
    console.log(a)
    a = () => {

    }
    // var已经声明提升了
    a = 2
    console.log(a)
}

foo(10)

输出10和2

这里注意的是我们的箭头函数和function是不一样的,上面说我们的function是变量声明提升,赋值也会提升,但是我们的箭头函数仅仅只会变量提升而已,因为()=>function关键字是不一样的,function是声明函数的关键字,()=>只是一个赋值的右值而已

回过头来再看这个例子

function foo(a) {
    console.log(a)
    function a (){

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

foo(10)

它输出的是function()和2

js编译后的执行顺序如下

//接收形参的传值
function foo(var a=10) {
    var a = function (){
    
    }
    console.log(a)
    var a = 2
    console.log(a)
}

foo(10)

好了,相信通过我的讲解,大家应该对变量提升有了新的认识了。我们下次再见,see you!

你可能感兴趣的:(前端,javascript)