js作用域详解

194 阅读2分钟

这是我参与更文挑战的第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
ES6中let创建块级作用域