浏览器原理:4.5作用域链和闭包

本文深入探讨JavaScript中的作用域链,解释了如何通过词法作用域确定作用域链,并通过示例展示了闭包的概念及其工作原理。闭包允许内部函数访问外部函数的变量,即使外部函数已执行完毕。同时,文章提到了闭包可能导致的内存泄漏问题,以及如何通过垃圾回收机制来管理闭包占用的内存。
摘要由CSDN通过智能技术生成

作用域链

  • 在每个执行上下文的变量环境中,都包含了一个外部引用(outer),专门用来指向另外的执行上下文,当一段可执行代码使用了一个变量,JS引擎会去执行上下文中查找该变量:
    • 首先会在当前的执行上下文中寻找该变量
    • 如果找不到就会去 outer 指向的执行上下文中去寻找该变量,如果仍然还是找不到就会抛出错误

我们把这个像链条一样的查找的顺序称为作用域链,要想了解作用域链,首先得先了解词法作用域,因为作用域链是由词法作用域来决定的

词法作用域

  • 词法作用域就是指 作用域是由代码中函数声明的位置来决定的,即在写好代码的那一刻,作用域就已经去确定好了,即词法作用域是静态的作用域,通过它就能来观测作用域链是怎样的

以下面一段代码作为示例

function baz(){
	console.log(b)
}

function foo(){
	let a = 2;
	function bar(){
		let b = 3; 
		console.log(a) // 2
		baz() // undifined
	}
	bar();
	console.log(b) // undifined
}
foo()

从这段代码可以很容易地看出,作用域以及作用域链是怎么样的。我们从执行上下文的角度来分析,如图
在这里插入图片描述
通过上面我们可以清楚地发现JS的作用域链就是由词法作用域来决定的,即是由代码的物理位置决定的

闭包

  • JS中,根据词法作用域的规则,内部函数总是可以访问其所在的外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,那么这些变量的集合就称为那个外部函数的闭包,比如外部函数为foo,则称为foo函数的闭包

通过实际代码来理解

function foo()
{
	var name = 'abc'
	let test1 = 1
	const test2 = 2
	var innerBar = {
		getName:function(){
			console.log(test1)
			return name
		},
		setName:function(newname){
			name = newname
		}
	}
	return innerBar
}

var bar = foo()
bar.setName('123')
bar.getName() // 1
console.log(bar.getName()) // 1 123
console.log(test1) // undifined

这里不难发现 明明 foo 函数已经执行完了,执行栈中的foo函数的执行上下文也被弹出了,但在调用 bar.getName() 打印 test1 的时候,仍然打印了 test1 的值,说明 test1 并没有被销毁,但在全局直接打印 test1 的时候,结果却是 undifined,说明 test1 只能够被 bar.getName() 所调用

下面从执行上下文的角度来分析闭包出现的时候的内存情况
在这里插入图片描述

getName会按照自己的执行上下文、foo闭包、全局执行上下文的作用域链来寻找变量

通过 Chrome devtool ,调试的时候打开 scope 也可以查看闭包,以及当前函数的作用域链

闭包的回收

通常,如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭,容易造成内存泄漏,但当把引用该闭包的变量的引用修改成null,JS引擎的垃圾回收器就会回收这块内存

如果引用闭包的函数是个局部变量,等函数销毁后,在下次JS引擎执行垃圾回收时,判断闭包这块内容如果已经不再被使用,那么JS引擎的垃圾回收器就会回收这块内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值