JS中的变量提升和函数提升

console.log(a);

var a = 1

如上面代码中可以看出,由于JS是从上到下一行行的执行,因此很多人看到这一串代码的时候,会认为由于log之前没有定义a,因此会输出underfind,的确,这段代码最终输出的结果确实是underfind,但是并不是没有定义a,而是在输出之前没有给a赋值的原因。那会有人说,不是JS是从上到下一行行的执行的嘛?定义a在输出的后面塞?那为什么最后确实先定义了a呢?其实这中间存在着变量提升 

1.变量提升

    引擎会在解释JavaScript代码之前首先对齐进行编译,编译过程中的一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来,这也正是词法作用域的核心内容。其实简单的来说,变量提升就是在编译的过程中,将变量的声明和函数的声明提升至对应作用域的顶端,即会优先执行。就拿下面的代码来说。其实在真正编译的过程中,代码的执行顺序并不是我们所写的这样,它会优先的将变量的定义,函数的声明给优先的执行。

//编译前

a = 1

console.log(a);

var a

//编译后

var a

a = 1

console.log(a);

  那么下面这段代码也应该就很明白了,第一次log出来的就是underfind,因为在它之前就只是先声明了a,而并没有给a赋值,导致a为underfind 

    console.log(a);

    var a = "我是函数外面的a";

    var foo = function(){

      console.log(a);

      var a = "我是函数里面的a";

    }

    foo();

2.函数的变量提升 

2.1函数声明式   

    在JS中不只是变量存在着变量提升,其实函数也存在。如下面的一段代码中可以看出,我们在函数定义前调用了函数,但是并没有报错,且在浏览器中还执行并打印了123,难道说在JS中我们可以先使用函数,后在定义函数吗?这不违背了函数中的先定义后使用???

   其实并没有违背,只是其中的函数提升帮助我们做了这件事。我们这样写之所以不会报错,是因为浏览器在编译的过程中,会优先的将定义的函数或者变量在其定义域中优先执行,因此即使我们把定义的函数写到前面还是后面,在编译的过程中,都是在最前面优先编译,后再执行对应的调用过程,因此即使采取下面这种写法,也不会出现报错

    test()

    function test(){

      console.log(123);

    }

 2.2函数表达式 

     函数表达式是声明函数的另一种写法,那它会不会存在着这种用法呢?,如下面代码所示,但是我们发现在执行函数的时候,会出现我所标记的两种报错。那又是为什么呢?的确,函数提升只是提升声明式的函数,不能提升表达式的函数。因此下面的代码其实在编译中的顺序就为如下,因为此时的foo2只是声明了,并没有给他赋值,因此foo2()就会出现相关的错误,就会出现不是一个方法。

console.log(foo2); // undefined

foo2(); // TypeError: foo2 is not a function

var foo2 = function () {

  console.log("foo2");

}

编译后

 var foo2

 console.log(foo2); // undefined

 foo2(); // TypeError: foo2 is not a function

 foo2 = function () {

    console.log("foo2");

 }

3.先后顺序

     我们可以从下面两个代码中可以看出,我们定义了两个foo2(),然后改变其调用的位置,无论是第一种还是第二种,都会出现  我是第一个foo2  因此我们可以看出函数提升的优先级会高于变量提升的优先级,否则就不会输出foo2  而是报错 foo2不是个函数。通过这个案例可以看出函数提升的优先级会高于变量提升的优先级

     但是我们一旦在后面调用,无论是那一种,如果是重名了的函数,那么调用的函数执行的是最后一次定义函数的内容,就如下面顺序的话,那么调用 foo2 执行打印后的结果是我是第二个foo2 

    foo2();

    function foo2() {

      console.log("第一个foo2");

    }

    var foo2 = function () {

      console.log("第二个foo2");

    }

    foo2();

    function foo2() {

      console.log("第一个foo2");

    }

    var foo2 = function () {

      console.log("第二个foo2");

    }

 4.let和const

     在ES6中更推荐我们使用let 或者const来代替var来声明函数或者变量,那么其中存在着变量提升吗?,如下,我们会惊奇的发现,会出现报错,大致的意思就是 使用let和const声明的变量我们需要在赋值后才能进行访问,即不能在赋值前进行访问。因此其实尽量采取let或者const来声明相关的变量和函数

console.log(a);

let a = 1

console.log(f22);

const f22 = function(){

  console.log(123);

}

 5.一个小小的案例 

    var foo = function (x, y) {

      return x - y

    }

    function foo(x, y) {

      return x + y

    }

    var sum = foo(1, 2)

    console.log(sum);

编译后的结果如下 结果为-1。你是否做过了呢?????

    function foo(x,y){

      return x+y

    }

    var foo

    var sum

     //覆盖了之前定义的函数

    foo = function(x,y){

      return x-y

    }

    sum = foo(1,2)

    console.log(sum);

总结

JS在编译的过程中首先会处理函数声明,其次会处理变量声明如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性

 最后留下一个思考题,答案可以在评论区中发表出来,一起学习学习(思考题取自于其他博客)

console.log(person)
console.log(fun)
var person = 'jack'
console.log(person)
 
function fun () {
    console.log(person)
    var person = 'tom'
    console.log(person)
}
fun()
console.log(person)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值