0. 前言

记一次这周在项目上遇到的一个问题,为解决这个问题我和朋友研究了大概两三个小时。我们都是第一次遇到这种问题,请大佬勿喷,下面我来讲一下这个问题的成因和解决的办法。

直接说结果[问题结论](#4. 问题解决)

1. 问题来源

在目前这个项目中有一个函数需要根据后端返回返回的数据,分别进行不同操作。因为要执行不同的操作的判断有点多,所以决定使用switch语句来进行判断。

一般来说switch语句是长这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
switch (expr) {
case "Oranges":
console.log("Oranges are $0.59 a pound.");
break;
case "Apples":
console.log("Apples are $0.32 a pound.");
break;
case "Bananas":
console.log("Bananas are $0.48 a pound.");
break;
case "Cherries":
console.log("Cherries are $3.00 a pound.");
break;
case "Mangoes":
case "Papayas":
console.log("Mangoes and papayas are $2.79 a pound.");
break;
default:
console.log("Sorry, we are out of " + expr + ".");
}

console.log("Is there anything else you'd like?");

我写的代码也是这样的格式,但是很奇怪的是在浏览器中运行会报一个非常奇怪的错误。

image-20230827172712360

提示我“未捕获(在promise中)ReferenceError:在初始化之前无法访问orderID”,就很奇怪哎,我这个orderID已经是在全局声明了,并且如果实在switch外面打印这个orderID是可以正常访问的。然后我开始怀疑是不是switch语句写错了?但是经过排查发现好像没有啥问题呀!然后我们就开始排错。

2. 问题排查

首先我们开始尝试在switch case内部去let一个orderID,发现还是会报上述错误。于是我们想到是不是是orderID这个变量名的问题呢?然后我们就随便声明一个变量,用这个随便声明的变量去接函数的返回值,发现这个随便声明的变量是可以正常访问的。这就我们感觉非常奇怪的,为什么会出现这种情况呢?接着我们就在想会不会是switch语句内部哪里出现了问题,然后我们开始挨个注释case判断是哪一个case出现了问题。然后最后发现case 1:被注释掉了这个判断就正常了,那么这个case 1:和其他的有什么不同呢?

image-20230827174333134

看到这,我也是没有搞明白为什么会这样?直觉来讲,其他的case也不会跑到case里面来找变量吧,然后就网上找文档到底是怎么一回事。

3. 问题结论

最后在网站找到一篇文章说case语句的变量声明是在整个switch语句中可见的

emmmm

这就很反直觉了。

这里引用部分原文

1、case语句的变量声明是在整个switch语句中可见的。

2、case语句中可以变量声明和定义,但在case语句中变量初始化的话有时会产生编译错误,原因是编译器为了避免“不一致”现象。具体解释如下:

  • 因为case语句中的变量声明、定义、初始化实在整个switch语句中可见的,变量声明实在编译时完成,而变量初始化需要在执行是完成,所以,如果在一个非最后一个case的case(命名case1)中进行了变量声明加初始化(如int a=0;),若真正执行的时候switch的条件值是该case后面的某个case(命名case2)的值,则case2中可以看到a的声明,也可以使用啊,但是因为没有执行case1,所以a没有初始化,所以在case2中使用的a并不是“预想的”a,这就产生了不一致。但如果在case1中只有声明或定义,而没有初始化,则不会产生这种不一致,编译时就不会报错。

这个可以通过编译,因为此时在case ‘b ‘中a不可见(因为a在语句快中),所以不会出现上述的“不一致”现象,因此编译顺利通过。

此时也会顺利通过编译,因为a的定义(初始化)放在了最后一个分支中,由于在case ‘b ‘后面没有其他分支了,所以同样不会出现上述的“不一致”现象,所以仍然顺利通过编译。

4. 问题解决

这里提供一个解决问题的方法

  • 不在case里面使用赋值解构
  • 或者是使用{}包住要判断的内

例如:

1
2
3
4
5
6
7
8
9
10
11
12
switch(val) {
case 1:{
赋值解构、变量声明...
}
break;
case 2:
...执行的操作
break;
default:
...
break;
}