let 定义的变量范围,为什么是这样的?

想问下 下面俩段代码 中,ts报错 为什么。

const expr = 'Papayas';
switch (expr) {
  case 'Oranges':
    let a = 123;
    console.log('Oranges are $0.59 a pound.',a);
    break;
  case 'Papayas':
    let a = 456;
    console.log('Mangoes and papayas are $2.79 a pound.',a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`);
}

const expr = 'Oranges';
switch (expr) {
  case 'Oranges':
    let a = 123;
    console.log('Oranges are $0.59 a pound.',a);
    break;
  case 'Papayas':
     a = 456;
    console.log('Mangoes and papayas are $2.79 a pound.',a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`,a);
}

{032F811F-3874-4328-8504-20287E6B34AE}.png
才发现第一种也是有问题的 ,好奇怪

我个人一直以为第一种写法是对的,但是ts报错提示我重复定义了,我就改成第二种写法写的,结果发现 build 以后(webpack) 就变成这样了,导致变量没有定义,为什么会有这样奇怪的行为

{C763F06D-1957-4BD5-BD29-B238A97699AD}.png

chunk代码就成这样了,代码中就会生成一个r 变量,这个变量没有定义就被使用。。。
{5E8B6A06-B936-45F9-8351-2FD5A0972B10}.png

有点搞不清楚了,大佬快来讲讲。

阅读 593
avatarAI BotBETA

回答

在 JavaScript(以及 TypeScript)中,let 关键字定义的变量具有块级作用域(block scope),这意味着它们仅在声明它们的块或任何包含的子块中可见。这与 var 关键字定义的变量不同,后者具有函数作用域或全局作用域。

第一段代码分析

const expr = 'Papayas';
switch (expr) {
  case 'Oranges':
    let a = 123;
    console.log('Oranges are $0.59 a pound.', a);
    break;
  case 'Papayas':
    let a = 456;
    console.log('Mangoes and papayas are $2.79 a pound.', a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`);
}

在这段代码中,每个 case 块都是一个独立的块级作用域。因此,尽管两个 case 块中都使用了变量名 a,但它们是在不同的作用域中声明的,互不影响。然而,TypeScript 会报错,因为它可能检测到潜在的重复声明问题(尽管在这个特定例子中,由于 case 分支互斥,实际上不会有问题)。这种报错可能是一种防御性编程措施,防止开发者在更复杂的逻辑中不小心重复声明变量。

第二段代码分析

const expr = 'Oranges';
switch (expr) {
  case 'Oranges':
    let a = 123;
    console.log('Oranges are $0.59 a pound.', a);
    break;
  case 'Papayas':
    a = 456; // 这里会报错,因为 `a` 在这个作用域内未定义
    console.log('Mangoes and papayas are $2.79 a pound.', a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`, a); // 同样,`a` 在这里也是未定义的
}

在这段代码中,变量 a 仅在 case 'Oranges' 块中被声明。当 expr 的值为 'Oranges' 时,代码运行正常。但是,如果 expr 的值为 'Papayas' 或任何其他值,JavaScript 引擎会在尝试访问 a 时抛出一个引用错误(ReferenceError),因为在当前作用域内 a 未被定义。

关于编译后的代码

在 Webpack 或其他 JavaScript 模块打包器中,代码可能会经过优化和重构,导致变量名改变或作用域调整。在你的例子中,生成的代码中出现了一个未定义的变量 r,这可能是由于编译器或打包器在处理原始代码时未能正确处理作用域或变量提升(hoisting)造成的。这通常是由于复杂的代码结构或编译器/打包器的特定行为导致的。

要解决这个问题,你应该确保变量在使用前已被正确定义和声明,并且理解你的构建工具如何处理这些代码。在 TypeScript 中,避免在 switch 语句的不同 case 块中重复声明同一个变量名,或者在需要跨多个 case 块共享变量时,在 switch 语句之前声明该变量。

1 个回答

这两段 TypeScript 代码的报错原因都与 JavaScript/TypeScript 的 块级作用域switch 语句特性 有关。

第一段代码报错原因:

const expr = 'Papayas';
switch (expr) {
  case 'Oranges':
    let a = 123; // 第一次声明 a
    console.log('Oranges are $0.59 a pound.',a);
    break;
  case 'Papayas':
    let a = 456; // ❌ 报错:重复声明块级作用域变量 'a'
    console.log('Mangoes and papayas are $2.79 a pound.',a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`);
}

原因:

  1. switch 语句的所有 case 共享同一个作用域,并不是每个 case 有独立的作用域。
  2. 当你在 case 'Oranges'case 'Papayas' 中分别用 let a 声明变量时,相当于在同一个作用域内重复声明同名变量 a
  3. let 声明的变量具有块级作用域,且在同一个作用域内不允许重复声明。

第二段代码报错原因:

const expr = 'Oranges';
switch (expr) {
  case 'Oranges':
    let a = 123; // 声明 a
    console.log('Oranges are $0.59 a pound.',a);
    break;
  case 'Papayas':
    a = 456; // ❌ 报错:变量 'a' 在声明前被使用
    console.log('Mangoes and papayas are $2.79 a pound.',a);
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`,a); // ❌ 报错:变量 'a' 未定义
}

原因:

  1. 变量 a 的声明被限制在 case 'Oranges' 的代码块中。虽然 switchcase 共享作用域,但 a 的声明只有在 case 'Oranges' 执行时才会发生。
  2. 如果 expr 的值是 'Papayas',代码会直接跳转到 case 'Papayas',此时 a 尚未声明,直接赋值 a = 456 会导致“变量在声明前使用”的报错。
  3. default 分支中访问 a 时,如果 a 未被任何 case 声明(例如 expr 是其他值),也会导致“变量未定义”的错误。

解决方案:

TypeScript 的报错是由于 switch 语句的 case 共享作用域导致的变量重复声明或作用域不明确。通过统一声明变量或用 {} 隔离作用域即可解决。

方法 1:统一声明变量

switch 语句外部声明变量,避免重复声明或作用域问题:

const expr = 'Papayas';
let a: number; // 统一声明
switch (expr) {
  case 'Oranges':
    a = 123;
    break;
  case 'Papayas':
    a = 456;
    break;
  default:
    a = 0; // 处理默认值
}

方法 2:为每个 case 创建独立作用域

{} 包裹每个 case 的代码,强制创建独立块级作用域:

const expr = 'Papayas';
switch (expr) {
  case 'Oranges': {
    let a = 123; // 仅在此块内有效
    console.log('Oranges are $0.59 a pound.',a);
    break;
  }
  case 'Papayas': {
    let a = 456; // 独立的块级作用域,不会冲突
    console.log('Mangoes and papayas are $2.79 a pound.',a);
    break;
  }
  default: {
    console.log(`Sorry, we are out of ${expr}.`);
  }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏