内容简介:作者:Dmitri Pavlutin
每日前端夜话 第324篇
翻译: 疯狂的技术宅
作者:Dmitri Pavlutin
来源:dmitripavlutin
正文共:1829 字
预计阅读时间:10 分钟
在 JavaScript 中,代码块、函数或模块为变量创建作用域。例如 if 代码块为变量 message 创建作用域:
1if (true) {
2 const message = 'Hello';
3 console.log(message); // 'Hello'
4}
5console.log(message); // throws ReferenceError
在 if 代码块作用域内可以访问 message 。但是在作用域之外,该变量不可访问。以上是作用域的简短介绍。
以下是 5 种有趣的情况,其中 JavaScript 作用域的行为与你预期的不同。你可能会研究这些案例以提高对作用域的了解,或者只是为面试做准备。
1. for 循环内的 var 变量
思考以下代码片段:
1const colors = ['red', 'blue', 'white'];
2
3for (let i = 0, var l = colors.length; i < l; i++) {
4 console.log(colors[i]); // 'red', 'blue', 'white'
5}
6console.log(l); // ???
7console.log(i); // ???
当你打印 l 和 i 变量时会发生什么?
答案
console.log(l) 输出数字 3 ,而 console.log(i) 则抛出 ReferenceError 。
l 变量是使用 var 语句声明的。你可能已经知道, var 变量仅受函数体作用域限制而并非代码块。
相反,变量 i 使用 let 语句声明。因为 let 变量是块作用域的,所以 i 仅在 for 循环作用域内才可访问。
修复
把 l 声明从 var l = colors.length 改为 const l = colors.length 。现在变量 l 被封装在 for 循环体内。
2. 代码块中的函数声明
在以下代码段中:
1// ES2015 env
2{
3 function hello() {
4 return 'Hello!';
5 }
6}
7
8hello(); // ???
调用 hello() 会怎样? (代码段在 ES2015 环境中执行)
答案
因为代码块为函数声明创建了作用域,所以在 ES2015 环境中调用 hello() 会引发 ReferenceError: hello is not defined 。
有趣的是,在 ES2015 之前的环境中,在执行上述代码段时不会抛出错误。 你知道为什么吗?请在下面的评论中写下你的答案!
3. 你可以在哪里导入模块?
你可以在代码块中导入模块吗?
1if (true) {
2 import { myFunc } from 'myModule'; // ???
3 myFunc();
4}
答案
上面的脚本将触发错误: 'import' and 'export' may only appear at the top-level 。
你只能在模块文件的最顶级作用域(也称为模块作用域)中导入模块。
修复
始终从模块作用域导入模块。另外一个好的做法是将 import 语句放在源文件的开头:
1import { myFunc } from 'myModule';
2
3if (true) {
4 myFunc();
5}
ES2015 的模块系统是静态的。通过分析 JavaScript 源代码而不是执行代码来确定模块的依赖关系。所以在代码块或函数中不能包含 import 语句,因为它们是在运行时执行的。
4. 函数参数作用域
思考以下函数:
1let p = 1;
2
3function myFunc(p = p + 1) {
4 return p;
5}
6
7myFunc(); // ???
调用 myFunc() 会发生什么?
答案
当调用函数 myFunc() 时,将会引发错误: ReferenceError: Cannot access 'p' before initialization 。
发生这种情况是因为函数的参数具有自己的作用域(与函数作用域分开)。参数 p = p + 1 等效于 let p = p + 1 。
让我们仔细看看 p = p + 1 。
首先,定义变量 p 。然后 JavaScript 尝试评估默认值表达式 p + 1 ,但此时绑定 p 已经创建但尚未初始化(不能访问外部作用域的变量 let p = 1 )。因此抛出一个错误,即在初始化之前访问了 p 。
修复
为了解决这个问题,你可以重命名变量 let p = 1 ,也可以重命名功能参数 p = p + 1 。
让我们选择重命名函数参数:
1let p = 1;
2
3function myFunc(q = p + 1) {
4 return q;
5}
6
7myFunc(); // => 2
函数参数从 p 重命名为 q 。当调用 myFunc() 时,未指定参数,因此将参数 q 初始化为默认值 p + 1 。为了评估 p +1 ,访问外部作用域的变量 p : p +1 = 1 + 1 = 2 。
5. 函数声明与类声明
以下代码在代码块内定义了一个函数和一个类:
1if (true) {
2 function greet() {
3 // function body
4 }
5
6 class Greeter {
7 // class body
8 }
9}
10
11greet(); // ???
12new Greeter(); // ???
是否可以在块作用域之外访问 greet 和 Greeter ? (考虑 ES2015 环境)
答案
function 和 class 声明都是块作用域的。所以在代码块作用域外调用函数 greet() 和构造函数 new Greeter() 就会抛出 ReferenceError 。
6. 总结
必须注意 var 变量,因为它们是函数作用域的,即使是在代码块中定义的。
由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import 语法(以及 export )。
函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。
在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。
希望这些陷阱能够帮你巩固作用域知识!
原文链接
https://dmitripavlutin.com/javascript-scope-gotchas/
2020年
京程一灯课程体系上新,这是我们第一次将全部课程列表对外开放。
愿你有个好前程,愿你月薪30K。我们是认真的 !
点击文末 阅读全文 查看细节。
长按二维码,加大鹏老师微信好友
拉你加入前端技术交流群
唠一唠怎样才能拿高薪
✎ 往期精彩回顾
从 JavaScript、ES6、ES7 到 ES10,你学到哪儿了?
以上所述就是小编给大家介绍的《面试中关于 JavaScript 作用域的 5 个陷阱》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 警惕 Go 编程陷阱
- PowerShell当心ToString()陷阱
- Python locals() 的陷阱
- Golang定时器陷阱
- Golang的WaitGroup陷阱
- Disable Function 的陷阱
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
大数据系统构建
Nathan Marz、James Warren / 马延辉、向磊、魏东琦 / 机械工业出版社 / 2017-1 / 79.00
随着社交网络、网络分析和智能型电子商务的兴起,传统的数据库系统显然已无法满足海量数据的管理需求。 作为一种新的处理模式,大数据系统应运而生,它使用多台机器并行工作,能够对海量数据进行存储、处理、分析,进而帮助用户从中提取对优化流程、实现高增长率的有用信息,做更为精准有效的决策。 但不可忽略的是,它也引入了大多数开发者并不熟悉的、困扰传统架构的复杂性问题。 本书将教你充分利用集群硬件优势的La......一起来看看 《大数据系统构建》 这本书的介绍吧!