JavaScript-一个经典的坑:条件式函数声明同名变量,块作用域,直接赋值,变量提升

    var e = 1
    if (true) {
      function e() {}
      e = 2
    }
    console.log(e);

问题1:为什么打印结果是函数?

问题2:为什么e=2的赋值操作失效了?

虽然代码只有几行,但是里面涉及的知识点异常的多.

代码运行环境: 谷歌浏览器(其它浏览器可能有不同的行为表现)

第一个知识点:

JS代码预解析与变量提升和函数声明提升。在全局作用域和块级作用域下,函数声明提升与函数声明语句执行有不同的行为。

  1. 在全局作用域下的函数声明语句,预解析时函数名与函数值直接挂到GO对象上,值就是声明的函数,在执行代码时不会再执行函数声明语句
  2. 在块级作用域下的函数声明语句,块作用域里代码执行前的预解析时,函数名与函数值挂到VO对象上,值是声明语句的函数,在执行代码时还会再执行函数声明语句。这里执行函数声明语句时只做一件事:就是把函数名此时的值赋值给GO对象里的同名变量
  3. 块级作用域里的函数声明在整个程序代码执行前,会把函数名挂到GO对象上,不论嵌套有多深,值为undefined,而它的真正的值,取决于块级函数声明执行时抛出的值

在执行代码前JS会对var声明的变量进行提升,除此之外还会对非函数作用域里的函数声明进行全局变量提升(对所谓块作用域进行全局变量提升是es5的行为,对es5来说没有块作用域,所以es6概念里的块作用域下的函数声明必须被提升到全局。但是如今es6引入了块作用域的概念,所以没有把块作用域下的函数声明值挂载到GO对象里,浏览器的这种怪异行为是为了兼容以前es5时代的代码)。(这一段话非常重要,是理解这段代码运行结果的基础)

预解析之后,在全局作用域里会出现一个变量e,且值为undefined。

这段代码中,var e 会在全局声明一次e,function e 也会在全局声明一次e(这里执行的是es5的行为,es5没有块作用域),都是相同变量名,且值都为undefined,可以理解为是等价的,没有谁覆盖谁。

关键在于后面的代码执行,会如何改变全局变量e的值?

代码执行到 if 语句里后:

最为关键的行为来了:块级作用域里的代码执行前,JS先对里面的函数声明进行提升,挂载到块级作用域里的VO对象里(这里执行的是es6的行为)。然后执行function e(){}这段代码,这段代码执行时候,会把自己的变量名e的值赋值给全局变量里的同名变量(这里是为了统一以上es5行为和es6行为导致的值的变化)。(这里非常重要,彻底改变了全局变量e的值)

执行 if 里的第二行代码:因为执行上下文的关系,e = 2的操作会先在块作用域里查到变量e,块作用域里有被函数声明的变量e,所以块作用域里的e被赋值成了2。

执行最后的打印代码:在全局里打印e,打印结果是全局e里的值,该值给赋值成了函数,所以打印出来是一个函数。

关于问题2:其实赋值操作没有失效,只是没有像我们想当然的那样以为会把函数值覆盖掉,或者说我们一直认为e=2操作的是全局变量,其实操作的是块作用域里的函数声明的变量e。

拓展知识点:

在预解析时,块级作用域下,函数声明会被提升到全局,且值为undefined。

在全局作用域下,函数声明的值为该函数。

主要是因为块级作用域的关系,把函数声明的行为割裂成了两种情况。而这个问题就是因为es6引入块级作用域引起的。

作用域里的声明提升在代码执行之前。

如果把以上代码修改一下,变成这样: 猜猜最后打印结果是什么?

    var e = 1
    if (true) {
      e = 2
      function e() {}
    }
    console.log(e);

e=2赋值操作放在函数声明之前。

前面的执行和上一段代码一样,关键在于块作用域里的执行顺序发生了变化,最后打印出来的结果上上一段代码完全不一样。

块级作用域里的代码执行前,作用域里的函数声明先会被挂载到VO对象里(拓展:全局作用域里,代码执行前函数声明先被挂载到GO对象里)

然后,e=2赋值操作会修改VO对象里函数名e的值,此时函数名e的值为2。

执行到函数声明语句,它会把VO对象里的函数名的值赋值给GO对象里的同名变量,此时GO对象里的e值就是2。

最后打印结果就是2,你猜对了吗?

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值