[译] 图解JS: 2. 变量提升Hoisting
原文: JavaScript Visualized(7 Part Series): 2. Hoisting - By Lydia Hallie
几乎每个JS开发者都听过hoisting,当遇到奇怪的错误去google搜索时,最后总是能在StackOverflow上得到类似的解答:“这个错误是hoisting引起的”🙃,那么什么是hoisting?(ps: scope将会在下一篇文章介绍,我喜欢让每一篇文章聚焦于小而具体的知识点)。
如果你是JS初学者,你可能有遇到过一些变量抛出undefind, 或Reference Error等的经历,不知所以。Hoisting通常被解释为一种将变量和函数声明提升到文件顶部的特性,但其实底层并不是真的这样挪动声明的位置,尽管最终的结果确实是这样的。
当JS引擎执行JS代码时,首先做的就是对代码中的数据域创建和分配内存。这个时候还没开始执行代码,而是做执行前的准备工作。其中函数声明和变量声明的存储方式是不同的,函数会存储一个指向完整函数体的指针(或者说引用)。
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/9b5c7069ccaf40c79d17bb3e50341706.webp)
变量有一些区别,ES6引入了两个新的关键词用来声明变量: let和const, 通过let和const声明的变量在存储时不会进行初始化。
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/0b3db2a505744c6da10ca0b9d2cf5e06.webp)
通过var声明的变量在存储时会用undefined进行初始化.
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/b6bcebb143034547a42b42c194e5dfd0.webp)
到这里创建阶段就完成了,开始执行代码。让我们看看在文件顶部(变量和函数声明之前)的3条console.log语句的输出结果会是什么。
因为函数在内存中存储了指向函数体的指针,因此我们可以在创造函数前使用它们。
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/347bda158dbd4038ae694fccd480dd12.webp)
当我们在声明前使用var变量,程序会简单的打出其默认初始值undefined! 然后这可能导致异常行为,在大多数情况下都是无意识进行了这样的使用操作(你可能想得到真正的初始值而不是内存分配的默认undefined)😬
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/eadb39a2f3d049aa97a562f76d1a1e8f.webp)
为了避免意外引用到一个undefined的未定义变量,就像上个例子使用var一样,我们可以转为使用const和let,这两个关键词有一个“暂时性死区”的概念,即在实际声明之前的“区域”,也就是在它们初始化之前,都是“dead zone”;在dead zone试图访问变量时,就会得到一个ReferenceError(这也包括ES6类!)。
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/7c427eb1911c4c2c9b3214e44977960d.webp)
当JS引擎执行到实际声明和赋值变量的语句时,内存中的初始值就会被更新,变成实际声明的初始值。
(下图编号应该是7,我会尽快更新😬)
![[译] 图解JS: 2. 变量提升Hoisting](https://static.blogweb.cn/article/bc5107131d424aea9d69e6da32021e03.webp)
搞定!快速回顾下:
- 在执行代码前,函数和变量都作为执行上下文存储在内存中,这被称为Hoisting.
- 函数被存储为函数名和一个指向其函数体的指针,
var变量被存储为变量名和初始值undefined,而let和const变量/常量存储时无初始值。
我们已经了解了在执行代码时发生了什么,现在我希望术语Hoisting对你来说不再那么模糊了。同样,如果仍然不太清楚也不要担心。用得越多就会越熟悉。有问题请随时找我帮忙,我很乐意帮助你!😃
转载自:https://juejin.cn/post/7207406497541767225