一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
什么是作用域
有关作用域的名词解释这里就不班门弄斧了,直接借用MDN上的原话。
当前的执行上下文。值 (en-US)和表达式在其中 "可见" 或可被访问到的上下文。如果一个**变量 (en-US)** 或者其他表达式不 "在当前的作用域中",那么它就是不可用的。 作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用。
词法分析
有关JavaScript的编译原理对你来说可能是很清晰,也有可能是你闻所未闻,这取决于你对编程语言的掌握程度。
词法分析的核心负责人就是编译器,也正是因为编译器的存在,JavaScript才可以被称为一门编译语言。当然,JavaScript编译发生的时间是在执行前,而不是构建打包阶段,这也是我们常常所说的 “编译执行”。
编译器的编译过程也就是作用域的产生时机。每当编译器遇到变量的时候,都会先去作用域中查询,然后在决定如何进行操作。比方说,如果是新增变量的话,编译器会去作用域中查询是否已存在,如果存在的话,编译器则会跳过变量声明;如果是变量赋值操作,编译器也会去作用域中查询这个变量是否存在,如果不存在的话,编译器就会抛出异常。
到这里,可能大家对作用域有了一个简单的理解,那就是集中管理变量集合的地方。
作用域链
作用域链其实跟原型链有点相似。两者不同的是,原型链是由于函数的继承产生的,而作用域链是由于函数或者代码块的嵌套产生的。
变量的查找规则跟原型方法的查找规则很相似,也是顺着作用域链逐层向上查找,如果在当前作用域中查找不到该变量,就会顺着作用域链向上查找,直到找到该变量。如果找到了全局作用域(作用域链的顶端)也找到该变量的话,那么作用域就会告诉编译器该变量不存在,紧接着编译器就抛出错误。单纯的描述可能不是很好理解,但是我相信看到了下面这个错误大家应该感到熟悉了:
Uncaught ReferenceError: a is not defined
at <anonymous>:1:1
@ VM170:1
作用域类型
函数作用域
函数作用域是我们日常开发中最常见的一种作用域了,每一个函数都形成一个单独的作用域。而在当前作用域中声明的变量集合可以在当前作用域或者下游作用域中访问、使用。
// 作用域
function parent() {
const a = 'parent';
// 作用域
function child() {
const b = a + 'child';
}
}
块作用域
另一个开发中比较常见的作用域就是块作用域了,每个代码块也都形成一个单独的作用域。比如说 if ... else ... 语句。
// 作用域
function parent () {
const a = 'name';
// 作用域
if (Math.random() > 0.5) {
const b = 'condition';
}
}
特殊作用域
在JavaScript的大家庭中,还是有一些特殊的成员存在的。这些成员也可以形成作用域,他们就是:evel和with。由于本身的安全和性能问题,极度不被推荐使用,甚至也被遗忘在了角落了。
打完收工!
有关我对作用域的理解就告一段落了,希望对大家有所帮助。如果有所收获的话,别忘记一键三连哟。