JS函数定义在if块中的`小坑`

场景

看下方代码的输出结果:

var a = 0
console.log("第一次输出a: ", a)
if (true) {
    a = 1
    console.log("第二次输出a: ",a)
    function a() {}
    a = 2
    console.log("第三次输出a: ", a)
}
console.log("第四次输出a: ",a)

结果:

第一次输出a:  0
第二次输出a:  1
第三次输出a:  2
第四次输出a:  1

可以看到,前三次的输出都根据我们的赋值分别输出了0、1、2,但是第四次输出的时候,不是最后一次赋值的2,而是1

解释

  1. 首先function a() {}的定义,会在执行if块的时候,提升到if块的第一行, 也就是在a = 1的前面,后面的a = 1,其实是对函数a做的赋值,将函数a改为了1。

  2. 当代码执行到function a() {}代码定义的这一行的时候,因为JS规定,块级作用域的里的函数声明会提升到其所在的作用域顶部中,这里就是if块外面的地方,这里会将内部函数a的值同步给外部的a。

(你所不知道的JavaScript)
image.png

此时if块外面有一个a, if块里面有一个a,这时候是存在两个a的,顺着上面的即使我们解释一下这些代码:

var a = 0 // 外部a
console.log("第一次输出a: ", a) //输出外部a
if (true) {
    // 这里js隐式的把function a的定义放到这里来了,此刻这里有一个内部a
    a = 1 // 将内部a由函数修改为1
    console.log("第二次输出a: ",a) // 此时输出的是内部a
    function a() {} // 执行到function a的声明处,此时会将if块中的a提升到外部去,也就是把内部a赋值给了外部a, 外部a此刻被修改了
    a = 2 // 此处修改的是内部a, 内部a被由1修改为2
    console.log("第三次输出a: ", a) // 输出内部a 也就是a
}
console.log("第四次输出a: ",a) // 输出外部a,由于if块中的声明同步,将外部a从0同步成了1

可以看到其实就是因为fucntion a声明的地方导致了外部a被修改,其余地方if块中修改的都是内部a,做好这区分就可以了,虽然从肉眼上我们看到的只有一个a变量,但其实运行起来的时候是有两个的。

验证

  1. debug执行到if块中的时候,可以看到block中直接出现了一个 function a的函数定义,这说明函数定义确实被提升到了块的最上层:

image.png

  1. 当执行完a = 1的时候,外部的a还是0,此时内部块中的function a已经变为了a=1,这说明此时修改的已经是内部的函数a了:

image.png

  1. 当执行完function a() {}这行代码的声明时,发现外部的a被改为了1, 这说明声明提升是存在的,确实将内部a的声明提升到了外面的地方,此刻外面已经有了a,所以直接将内部a的值给了外部a:

image.png

  1. 再次执行a = 2时,可以看到此时修改的也是内部的a, 内部的a被修改了,外部的a而不受影响:

image.png

  1. 当最后一次输出a时,块已经被销毁,作用域又来了外面,所以输出的是外部a,而外部a在这之前已经被改为1,所以输出的是1

image.png

疑问

有的同学会疑问,为什么执行function a(){}的时候,已经把内部a提升到外部去了,这时候内部a,外部a是一样的,但是后面修改内部a为2的时候,怎么没有同步到外部a去呢,他两难道不是同一个吗?

他俩确实不是同一个,外部a内部a因为所在的作用域不同,他们的声明也是在不同的存在地址中的,

所以当修改内部a = 2的时候,改的是内部a这个变量内存地址的值,不会同步到外部a这个变量的内存地址中去,除非外部a内部a指向的是同一个对象,并且在if块中修改二者都指向的引用对象里面的值:

var a = 0
console.log("第一次输出a: ", a)
if (true) {
    a = {name: '我是一个引用对象', desc: '此时我被赋值给了内部a'}
    console.log("第二次输出a: ",a)
    function a() {}
    a.desc = '我在if块中修改了desc'
    console.log("第三次输出a: ", a)
}
console.log("第四次输出a: ",a)

输出

第一次输出a:  0
第二次输出a:  { name: '我是一个引用对象', desc: '此时我被赋值给了内部a' }
第三次输出a:  { name: '我是一个引用对象', desc: '我在if块中修改了desc' }
第四次输出a:  { name: '我是一个引用对象', desc: '我在if块中修改了desc' }

因为外部a和内部a此时都指向同一个对象,当内部a更改该对象的属性值时,外部a打印时自然也会输出更改后的结果。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值