这是我参与更文挑战的第3天,活动详情查看:更文挑战
作用域详解
理解作用域,首先需要理解程序的运行过程中的几个重要角色
-
引擎
从头到尾负责整个JavaScript程序的编译及执行过程
-
编译器
负责语法解析、代码生成
-
作用域
负责收集并维护由所有声明的变量组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限
下面举个小栗子来说明三者之间的联系
var a = 2
运行这行代码时会执行两个操作:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后再运行阶段引擎会在作用域中查找该变量,如果能找到就会对其赋值(未找到赋值为undefind)。
词法作用域和动态作用域
词法作用域:词法作用域就是定义在词法阶段的作用域,或者说是,词法作用域是由你写代码时将变量和块作用域写在哪里决定的,因此当词法分析分析器处理代码时会保持作用域不变。
动态作用域:动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心在何处调用,或者说是,作用域链是基于调用栈的,而不是代码中的作用域嵌套。
JavaScript使用的是词法作用域
函数作用域
含义:属于这个函数的全部变量都可以在整个函数范围内使用及复用
“隐藏”变量和函数
function foo(a) {
var b;
b = a + bar(a * 2);
function bar (a) {
return a + 1;
}
return b
}
foo (2) // 7
b // undefind
bar() // undefind
// 全局变量中无法访问函数内部变量与函数
规避冲突
模块化方案
立即执行函数(IIFE)
// 避免变量污染全局
var a = 2;
(function IIFE (glob) {
var a = 3
log( a ) // 3
log( glob.a ) // 2
})(window)
// 解决undedind被覆盖
undefind = true
(function IIFE ( undefind ) {
var a;
if (a === undefind) {
log( "a is undefind" )
}
})()
// 倒置代码运行顺序
var a = 2;
(function IIFE ( def ) {
def (window)
})(function (glob) {
var a = 3
log( a ) // 3
log( glob.a ) // 2
})
块作用域
“假”块级作用域
var foo = true
if (foo) {
var bar = foo * 2
}
console.log('bar', bar) // 仍然能访问 bar ,var 声明的变量写在哪里都是一样的,这仅仅只是看起来像是块级作用域,将var修改为let则会访问不到bar
try/catch块作用域
ES3中规定的 try/catch 会创建块级作用域
try {
undefind()
} carch (err) {
log (err) // 能正常执行
}
log(err) // err not find