web前端高级 - 两道题彻底理解闭包的作用域

287 阅读5分钟

闭包的作用域

为了更好的理解闭包的作用域,下面我们用一道题来展示闭包函数的执行过程和闭包的作用域

题一

let x = 5;
function fn(x){
	return function(y){
		console.log(y + (++x));
	}
}
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);
  • 文字解析:
  • 1.首先不管三七二十八,上来还是先开辟一块栈内存(执行环境栈ECStack),用来供代码执行
  • 2.然后形成一个全局上下文EC(G),全局上下文中有存储全局变量的全局变量对象VO(G),第一行代码执行创建值5和变量x并做关联
  • 3.创建函数fn,开辟一块堆内存(0x000)用来保存函数内部的代码字符串,其作用域为全局上下文EC(G)。(函数在哪里创建,哪里就是它的作用域)
  • 4.fn函数执行并将返回值赋值给全局变量f。函数fn的执行会形成一个独立的私有上下文EC(FN1),在私有上下文中有个活动对象AO(FN)用来保存函数的私有变量,在fn函数中形参x就是私有变量,实参6就是x的值(x=6)。
  • 5.函数fn执行创建了一个匿名函数,开辟一块新的堆内存(0x001)用来保存函数内部的代码字符串,同时将函数的地址返回并赋值给全局变量f。匿名函数的作用域为:函数fn的私有上下文。
  • 6.接着执行函数f(7),也就是上一步中fn返回来的匿名函数。同样也会形成一个独立的私有上下文EC(F1),形参y为函数的私有变量,并赋值为7(y=7),然后执行函数中的代码console.log(y + (++x));y为当前上下文中的私有变量值为7,x在当前上下文中不存在,于是到上级上下文EC(FN1)中查找得到值为6,因此输出结果为 14
  • 7.代码继续向下执行,此时fn函数第二次执行,并形成一个全新的私有上下文EC(FN2),并将形参x的值赋值为8(x=8)。同时创建一个新的匿名函数,开辟一块新的堆内存(0x002)用来保存函数内部的代码字符串。并将内存地址返回。其作用域为:EC(FN2)
  • 8.紧接着fn(8)执行完成后,由fn(8)返回的匿名函数(0x002)开始执行。形成私有上下文EC(ANY),并赋值9给形参变量y,继续执行函数内部的代码console.log(y + (++x));此时y为当前上下文中的私有变量值为9,x在当前上下文中不存在,于是到上级上下文EC(FN2)中查找得到值为8,执行自加后变为9,因此输出结果为 18
  • 9.下一行代码f(10)执行,此次执行与第6步f(7)执行类似还是同一个作用域,但不同的是上级上下文中的变量x的值在第6步执行完成后已经自加变为7(x=7),而y的值是本次传入的10(y=10),因此本次执行x再自加一次变为8(x=8),所以最后输出结果为 18
  • 10.最后执行console.log(x),此时的x是全局上下文中的全局变量x值为5,它并没有受到函数执行的影响,因此最后输出结果还是 5
  • 一图解真相 在这里插入图片描述

题一扩展

在上题中如果将函数fn的参数x去掉,会是什么样的结果呢? 提示:如果将参数x去掉那么所有的过程操作的都是全局变量x

let x = 5;
function fn(){
	return function(y){
		console.log(y + (++x));
	}
}
let f = fn();
f(7);
fn()(9);
f(10);
console.log(x);

题二

let a = 0, b = 0;
function A(a){
	A = function(b){
		alert(a + b++);
	}
	alert(a++);
}
A(1);
A(2);
  • 文字解析:
  • 1.首先依然是先开辟栈内存(执行环境栈ECStack)供代码执行,形成全局上下文EC(G),全局上下文中有个全局变量对象VO(G);
  • 2.代码执行创建变量a和b并赋值为0。创建函数A,开辟一块堆内存0x000保存函数A中的代码字符串。作用域为EC(G)
  • 3.函数A(1)执行,形成一个独立的私有上下文EC(A1),EC(A1)中有用来保存私有变量的私有变量对象AO(A1),并将形参a作为私有变量赋值为1(a=1)。
  • 4.函数A(1)继续往下执行,在EC(A1)中创建函数,开辟一块新的堆内存0x001保存函数内的代码字符串,其作用域为EC(A1)。执行alert(a++),弹窗输出字符串1(alert输出会自动转换为字符串,a++是先运算后自加所以alert时a的值仍是1);
  • 5.A(1)函数执行完成后,函数A(2)执行,形成新的独立的私有上下文EC(A2),此时的A已经指向了新的函数0x001这个堆,所以执行的代码是alert(a + b++),此时b的值为传入的参数2(b=2),而a在当前上下文中不存在,所以会去上级上下文中查找,当前上下文的上级上下文为EC(A1),EC(A1)中的变量a经过自加后变为2(a=2),所以最终A(2)输出结果为字符串4。
  • 一图解真相 在这里插入图片描述