Skip to content

作用域

  • 全局作用域
  • 局部作用域
  • 块级作用域
  • 执行作用域

1、 全局作用域

在任何地方都能访问的变量,其就是全局变量,其作用域就在全局作用域中

  1. 不使用 var 申明的变量
  2. 所有 window 对象的属性
  3. 在函数外定义的变量--ES5 try{}catch()

2、作用域链与执行作用域

什么是作用域链

javascript
var name = 'name';
var top = 'top';

function fun1(name, a) {
	var name = 'fun1';
	var age = 12;
	var qita = 'qita';
	function fun2() {
		var address = 'address';
		console.log(age);
		console.log(top);

		function fun3() {
			var fun3val = 'fun3val';
			console.log(address);
			console.log(top);
			console.log(name);
		}
		console.dir(fun3);
	}
	console.dir(fun1);
	console.dir(fun2);
	fun2();
}
fun1();
console.dir(fun1);

我们看 fun1 的函数属性,发现其存在一个[[Scopes]]属性,其就是函数创建的时候生成一个保存其作用域的属性数组

image 可见其没有调用前数组的第一个保存的值为上级作用域的值

keyvalue
name'name'
fun1function(){}

image

fun1(name,'这是a');

然后我们执行到 fun1()的时候,此时会创建一个 ‘执行上下文’ 的 内部对象,运行期上下文定义了运行时的环境对象。对于函数的每一次执行来说,其执行上下文都是唯一的,都有自己的独有的作用域链,当执行结束,执行上下文会被销毁。

当一个执行上下文创建的时候,其会复制一份函数创建时的[[Scopes]]数组,并且生成一个活动对象(Active Object),此活动对象放在[[Scopes]]的最上方,包含了所有的局部变量、命名参数、参数集合和 this

此时又创建了一个新的函数 fun2 其重复上面的步骤

image

image

然后当执行到 fun2()的时候

fun2();

image

可见作用域链就是 我们函数创建的时候 生成[[Scopes]]数组属性 其 0 -> n 分别存放了 函数父作用域直到 window 的 对象,当执行函数的时候,会复制[[Scopes]]生成一个独有的'执行上下文', 并且创建了一个保存了局部变量、命名参数、参数集合 arguments、this 的活动对象(Actived Object),并将其 shift 到[[Scopes]]属性中

函数执行时,如何知道变量的值?

就是通过 执行上下文的作用域链,查找同名标识符,搜索从前到后查找

image

如上图我们查找 address 的值 先从 [[Scopes]][0]查找,找到了 address 就不继续了; 如果查找 top,就需要先从 [[Scopes]][0] -> [[Scopes]][1] -> [[Scopes]][2]。

<font class="j-font-red j-font-blod" >任何属性的寻找都是消耗性能的,如我们访问 document 对象,就需要从作用域链的[0] ---> [length -1] 所以当我们在函数中需要多次使用较深变量的时候最好将其定义成自己的局部变量</font>

javascript
var dc = document;

改变作用域链

with

改变函数类作用域的指向

javascript
function fun4() {
	var name = 'name';
	with (document) {
		var body = body;
		var withEl = getElementById('with');
		console.log(name);
	}
}
fun4();
fun4 执行上下文作用域

image

with 的执行作用域

image

本来对于 fun4 执行的时候 我们访问 name 只需要在其活动上下文 AO 中就可以找到了,但是 我们使用了 with 其 临时生成了一个作用域并 shift 到[[Scopes]]的最上方,导致我们访问 name 需要从 [[Scopes]][0] -> [[Scopes]][1]

try{} catch(e){} catch 改变上下文作用域

javascript
function fun4() {
	var name = 'name';
	try {
		asdasd;
	} catch (e) {
		console.log(e); //ReferenceError: asdasd is not defined
		console.log(name);
	}
}
fun4();

其也跟 with 一样 在 catch 执行的时候 生成一个临时的活动对象,然后 shift 到[[Scopes]]数组中,导致我们访问 name 需要从 [[Scopes]][0] -> [[Scopes]][1],当执行完成 再销毁此活动对象

词法作用域和动态作用域

https://www.cnblogs.com/xiaohuochai/p/5700095.html