发现了 var 和 let/const 的一个细微差别

2023-08-30 17:33:15 +08:00
 liberty1900

大多数前端开发者都知道在浏览器里全局对象是 window,这点可以用 Object.is(globalThis, window) 来验证

如果在浏览器的全局环境下定义一个变量,那么它将被自动挂载到 window 对象上

var name = "Mike";

console.log(window.name)

但 let 和 const 却不会这样

  let age = 10;
  const sex = "Male";
  console.log(window.age, window.sex)
1533 次点击
所在节点    JavaScript
15 条回复
thinkershare
2023-08-30 17:34:16 +08:00
不要用 var 这种垃圾。
hsfzxjy
2023-08-30 17:35:23 +08:00
是这样的,而且不只这个差别
rrfeng
2023-08-30 17:39:50 +08:00
var 就是全局变量,千万别用。
liberty1900
2023-08-30 17:47:44 +08:00
用惯了了 VS Code ,里面 Cmd + Enter 是 Insert Line Below ,V 站这里直接给我发帖了。。。
Jirajine
2023-08-30 17:49:17 +08:00
你不用管它有什么区别,你只需要记住 var 是 deprecated ,永远不要使用就行了。
liberty1900
2023-08-30 17:53:59 +08:00
let/const 是 block scope 的,我以前理解所谓 block scope 就是用花括号形成的 scope 比如:

```javascript
{
let age = 10;
console.log(age); // 10
}
console.log(age) // undefined
```

如果我没有显示套上大括号,也就没有了 block scope 但是变量总得有个 scope ,那这个 scope 是不是 global scope 呢?看来我理解错了
HelloWorld556
2023-08-30 17:54:25 +08:00
chengxy
2023-08-30 17:58:10 +08:00
继续往下,学学 let 和 const 的区别
Adicwu
2023-08-30 18:01:12 +08:00
这你就得问问变量提升了
liberty1900
2023-08-30 18:04:29 +08:00
@liberty1900 一种合理的解释是 let/const 一定是 block scope 的,即使没有显式用花括号声明也是 block scope, 只是这种 scope 是隐式的,而且和 global scope 有区别,不能混为一谈
liberty1900
2023-08-30 18:06:56 +08:00
@chengxy 我知道 let 和 const 的区别,也知道 const 的局限性以及如何用 Object.create/Object.definedProperties/Object.seal/Object.freeze 来实现不同程度的不可变
cmdOptionKana
2023-08-30 18:11:06 +08:00
年轻人真幸福啊,没经历过只有 var 的年代。
NerbraskaGuy
2023-08-30 18:31:18 +08:00
这个不算细微吧,let 和 const 被造出来基本就是为了解决 var 这个痛点的
chnwillliu
2023-09-04 11:03:33 +08:00
Spec 都定义好了,参见 Execution Context 的 LexicalEnvironment 和 VariableEnvironment 。他俩都是 Environment Record ,但是装的东西不一样,一个负责 let const 声明的东西,一个负责 variable statement ,hoisting 还有 with block 的差异处理都在这里规定好了。

不光这样,全局还有个特殊的 Environment Record 叫 Global Environment Records, 全局 var 和 function 会变成 global object 的属性的行为就是在这里定义的。
chnwillliu
2023-09-04 19:15:37 +08:00
仔细翻了下 Spec ,还是要纠正一下,无论 let const 还是 var ,或者是 function declaration ,class declaration ,这些 identifier 都会在 LexicalEnvironment 中记录,要查找 identifier 也只要查找 LexicalEnvironment 。而 VariableEnvironment 是专门用来管理 variable statement 的,相当于额外放一份,用来处理一些老旧 feature 比如 eval 。

let/const 和 var 的区别在于在初始进入一个 function 或者 script 的时候,只有 top level 的 let/const 会被加入到 LexicalEnvironment ,而 variable statement 声明的 identifier 和 hoistable declaration 声明的 identifier 比如 function 声明,是会在最开始就全部加入到 LexicalEnvironment 中。

实例化一个 block declaration 后,需要创建一个新的 LexicalEnvironment 给到当前的 execution context ,后续的 let/const 是加入到这个 LexicalEnvironment 中了的。比如 for ( let i=0; i<10; i++) { let j; } 这里其实创建了 11 个临时 LexicalEnvironment ,for 语句给 i 创建了一个,每次循环会创建另一个新的,放一个 j (然后销毁)。而 var 是不存在这种考量的,无论几个 for 嵌套,都会在最开始就放入顶层的 LexicalEnvironment 。

不光在开始执行一个 function 会这样处理,在新的 script element ,module 或者 worker 开始执行,都是类似处理。这个顶层 scope 或者说 LexicalEnvironment , 就是 Global Environment ,Module Environment 和 Function Environment 。

Global Environment 在每个 realm 中只有一个,在 user land 和 globalThis 是等价的,而 var 变量 和 function declaration 的 identifier 能用 globalThis 直接当属性访问也是 Global Environment Record 做的特殊处理,参见 CreateGlobalVarBinding 和 CreateGlobalFunctionBinding 。

Function Environment 在每次 call 一个 function 的时候都会创建,module 同理,在第一次 import 时创建。

一个有趣的例子,这个 early error 是如何产生的呢?

```
(function(){

{

{
{
{
var b
}
}

let b;
}
}

})()
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/969547

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX