跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

5个关于JS作用域的陷阱(总结)

在 JavaScript 中,代码块、函数或模块为变量创建 作用域 。例如
if
代码块为变量
message
创建作用域:
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
在
if
代码块作用域内可以访问
message
。但是在作用域之外,该变量不可访问。 好的,这是作用域的简短介绍。如果你想了解更多信息,建议阅读我的文章 用简单的词解释 JavaScript 作用域 。 以下是 5 种有趣的情况,其中 JavaScript 作用域的行为与你预期的不同。你可能会研究这些案例以提高对作用域的了解,或者只是为面试做准备。 1.for循环内的var变量 思考以下代码片段:
const colors = ['red', 'blue', 'white']; for (let i = 0, var l = colors.length; i < l; i++) { console.log(colors[i]); // 'red', 'blue', 'white' } console.log(l); // ??? console.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. 代码块中的函数声明 在以下代码段中:
// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
调用
hello()
会怎样? (代码段在 ES2015 环境中执行) 答案 因为代码块为函数声明创建了作用域,所以在 ES2015 环境中调用
hello()
会引发
ReferenceError: hello is not defined
。 有趣的是,在 ES2015 之前的环境中,在执行上述代码段时不会抛出错误。 你知道为什么吗?请在下面的评论中写下你的答案! 3. 你可以在哪里导入模块? 你可以在代码块中导入模块吗?
if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
答案 上面的脚本将触发错误:
'import' and 'export' may only appear at the top-level
。 你只能在模块文件的最顶级作用域(也称为 模块作用域 )中导入模块。 修复 始终从模块作用域导入模块。另外一个好的做法是将
import
语句放在源文件的开头:
import { myFunc } from 'myModule'; if (true) { myFunc(); }
ES2015 的模块系统是静态的。通过分析 JavaScript 源代码而不是执行代码来确定模块的依赖关系。所以在代码块或函数中不能包含
import
语句,因为它们是在运行时执行的。 使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件 如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Andr​​oid友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Andr​​oid应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更 下载 4. 函数参数作用域 思考以下函数:
let p = 1; function myFunc(p = p + 1) { return p; } myFunc(); // ???
调用
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
。 让我们选择重命名函数参数:
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
函数参数从
p
重命名为
q
。当调用
myFunc()
时,未指定参数,因此将参数
q
初始化为默认值
p + 1
。为了评估
p +1
,访问外部作用域的变量
p
:
p +1 = 1 + 1 = 2
。 5. 函数声明与类声明 以下代码在代码块内定义了一个函数和一个类:
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
是否可以在块作用域之外访问
greet
和
Greeter
? (考虑 ES2015 环境) 答案
function
和
class
声明都是块作用域的。所以在代码块作用域外调用函数
greet()
和构造函数
new Greeter()
就会抛出
ReferenceError
。 6. 总结 必须注意
var
变量,因为它们是函数作用域的,即使是在代码块中定义的。 由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用
import
语法(以及
export
)。 函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。 在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。 希望这些陷阱能够帮你巩固作用域知识! 英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/作者:Dmitri Pavlutin 更多编程相关知识,请访问: 编程入门 !!

相关文章