JavaScript 变量提升

JavaScript 变量提升

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

这几天在刷题的过程中经常碰到关于变量提升作用域的问题。

作用域问题,在之前的博客有写过:Mr.J--JS学习(闭包私有化)

通常JS引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明及函数声明提升至当前作用域的顶端,然后进行接下来的处理。(注:当前流行的JS引擎大都对源码进行了编译,由于引擎的不同,编译形式也会有所差异,我们这里说的预编译和提升其实是抽象出来的、易于理解的概念)。

var val = 12;
function foo(){
    console.log(val);
    var val = 20;
    console.log(val);
}
foo();

我刚开始看这段代码,以为是作用域的问题,然后认为输出是: 12 20;

但是这段代码通过变量提升,可以转化成以下的代码:

var val = 12;
function foo(){
    var val;
    console.log(val);
    var val = 20;
    console.log(val);
}
foo();

答案很明显了:undefined 20

变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部。

JavaScript 初始化不会提升

var x = 5; // 初始化 x

elem = document.getElementById("demo"); // 查找元素 
elem.innerHTML = x + " " + y;           // 显示 x 和 y

var y = 7; // 初始化 y

在这段代码中,在使用y的值之后出现了y值的初始化,转化代码格式:

var x = 5; // 初始化 x
var y;     // 声明 y

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y;           // 显示 x 和 y

y = 7;    // 设置 y 为 7

最终输出:x 为:5,y 为:undefined

函数提升

function bar() {
    foo(); // output: I am hoisted

    function foo() {
        console.log('I am hoisted');
    }
}

bar();

为什么函数可以在声明之前就可以调用,并且跟变量声明不同的是,它还能得到正确的结果,其实引擎是把函数声明整个地提升到了当前作用域的顶部,预编译之后的代码逻辑如下:

// 预编译之后
function bar() {
    function foo() {
        console.log('I am hoisted');
    }

    foo(); // output: I am hoisted
}

bar();

其实这个就是执行上下文的有关问题了。好多学习JavaScript的人一般不会注意变量提升的问题,这样就会导致代码极其容易出错。

之前写过相关的博客,博客链接:一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))

同样,如果出现同名函数,将会进行函数的覆盖,即后者覆盖前者:

function bar() {
    function foo() {
        console.log(1);
    }

    foo(); // output: 2

    function foo() {
        console.log(2);
    }
}

bar();

为什么要进行提升

由于第一代JS虚拟机中的抽象纰漏导致的,编译器将变量放到了栈槽内并编入索引,然后在(当前作用域的)入口处将变量名绑定到了栈槽内的变量。(注:这里提到的抽象是计算机术语,是对内部发生的更加复杂的事情的一种简化。)

然后,Dmitry Soshnikov又提到了函数提升,他提到了相互递归(就是A函数内会调用到B函数,而B函数也会调用到A函数)。函数提升就是为了解决相互递归的问题,大体上可以解决像ML语言这样自下而上的顺序问题。

总结

function jsFun6(){  //函数声明和函数表达式的区别
    
    test1();//函数声明提升,在执行代码之前会先读取函数声明,不会报错
    function test1(){//函数声明方式创建函数
        alert("测试1");
    }
    
    //test2();报错,函数还不存在
    console.log(test2)//不会报错,变量提升只是提升变量的声明,并不会把赋值也提升上来,输出undefined
    var test2=function(){
        alert("测试2");
    };//使用函数表达式创建一个匿名函数(实际是以变量test3命名的函数)
    test2();//不会报错,以创建函数
                
    var test3=function(){
        alert("测试3");
    }();//加了括号立即执行
    
    var test4 = 12;// !注意看,一旦变量被赋值后,将会输出变量
    //函数提升优先级高于变量提升,所以函数先提升,然后变量提升覆盖之前的函数声明,表                
    //现为变量
    function test4() {
        alert("测试4");               
    }
    console.log(test4); //12

    var test5="test5_1";
    (function(){
        //js中的变量搜索顺序:找变量时,先找局部变量,如果没有局部变量;再找全局变量。
    alert(test5);//此时的test5为局部变量的提升,undefined
    var test5="test5_2";
    })();
                
}

只要理解了执行上下文,对于变量提升就会一下看懂。一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值