动图学 JavaScript 之:声明提升(Hoisting)

背景

JS 由于语言设计的缺陷(工期不够?),里面有一些堪称神奇的特性,初学者碰到后可能会满脸黑人问号,今天要介绍的就是其中的一个特性:声明提升(Hoisting)。

如果你是一个 JS 新手,有时候会碰到 undefined 或者 ReferenceErrors 错误,而声明提升有可能就是罪魁祸首。

声明提升常常被解释为:把变量和函数放到文件的顶部,虽然表面上看起来是这样,但事实却不是如此。

编译阶段

当 JS 引擎开始解析我们的脚本,第一件事就是为我们代码中的变量 设置内存。这个阶段代码还 没有运行,只是在为后面的代码执行做准备,这个阶段就是 编译阶段

这一阶段的一部分工作就是 找到所有的声明,并用合适的作用域将它们关联起来(有关作用域的部分我们下篇再讲)。

比如 var a = 2; JS 引擎会将其看作两个声明:var a;a = 2;。第一个定义声明在编译阶段进行,第二个赋值声明会被 留在原地 等待执行阶段。

而在编译阶段中,函数声明和变量的声明存储方式是不同的。

函数声明的存储方式

函数声明的存储,在内存中存储的是整个函数的引用。(注意 函数声明函数表达式 的区别)

下图的代码,我们先看其中的函数 sum 的声明:

1-functions.gif

let 和 const 声明的变量的存储方式

变量的存储不太一样。ES6 中引入了两个新的关键字:letconst,凡是用这两个关键字定义的变量,存储的值为 uninitialized

2-let-and-const.gif

var 声明的变量的存储方式

var 声明的变量,存储的时候默认值为 undefined

3-var.gif

执行阶段

现在编译阶段已经完成,JS 引擎该执行代码了。我们在文件顶部添加三个 console.log 语句。

提前调用函数

上面讲过,在编译阶段由于 函数声明 存储的是整个函数的引用,所以即使在函数声明之前也可以调用函数。

4-console.gif

提前使用 var 定义的变量

如果我们提前使用 city 这个变量,就会打印出 var 关键字定义的默认值 undefined。而大多数情况下,这个行为是令人困惑的,因为你并不期望它的值是 undefined

5-console-var.gif

提前使用 const 和 let 定义的变量

正是为了解决 var 的问题,所以才会有了 constlet,当我们提前访问它们定义的变量的默认值 uninitialized 时,就会抛出 ReferenceError 。这个行为有一个霸气的名字:临时死区(Temporal Dead Zone),即你不能在这个变量初始化前访问它。

6-console-let.gif

继续执行赋值操作

当 JS 引擎继续往下解释代码时,解释到某一行有赋值语句时,即会将内存中的值覆盖为代码中定义的值。

7-go-on.gif
(上图中编号应该是 7 哈~)

总结

来回顾一下:

  1. 函数和变量在 编译阶段 会将声明部分存储在内存中,这就是 声明提升 (其中还涉及到 执行上下文 的概念,我们下篇再讲)
  2. 函数声明存储的是整个函数的引用,var 声明的变量存储的默认值是 undefinedletconst 声明的变量的默认值为 uninitialized

不知道有没有讲明白呢?本文翻译于作者 Lydia Hallie 的系列文章,部分内容作了详细解释,下面还有三篇,欢迎订阅公众号关注更新哈:

参考资料


本文首发于公众号:码力全开(codingonfire)

关注并回复 副业, 获取技术人的副业秘籍

codingonfire.jpg


码力全开
尽我所能为大家带来有用的东西~ 欢迎关注公众号:「码力全开」

You know nothing, SpongeBob.

6.2k 声望
4.5k 粉丝
0 条评论
推荐阅读
如何批量 git pull 某个文件夹中的 git 仓库?
上代码用 shell 脚本可以比较方便实现:首先新建脚本:pull-all.sh {代码...} 填入如下内容: {代码...} 然后改一下权限: {代码...} 接下来就可以直接运行了: {代码...} 也可以不跟参数,会提示你输入目录~注意...

savokiss阅读 1.5k

封面图
手把手教你写一份优质的前端技术简历
不知不觉一年一度的秋招又来了,你收获了哪些大厂的面试邀约,又拿了多少offer呢?你身边是不是有挺多人技术比你差,但是却拿到了很多大厂的offer呢?其实,要想面试拿offer,首先要过得了简历那一关。如果一份简...

tonychen152阅读 17.7k评论 5

封面图
正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青56阅读 8.5k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy48阅读 7k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木75阅读 7.1k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs42阅读 6.8k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木45阅读 8.5k评论 6

You know nothing, SpongeBob.

6.2k 声望
4.5k 粉丝
宣传栏