作用域作用域链深层次理解到闭包的底层(深度好文)

作用域

作用域分为全局作用域,局部作用域和块级作用就是一个变量作用的范围

1.全局作用域

(1)全局作用域在页面打开时被创建,关闭时销毁
(2)在script标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问。
(3)在作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用
(4)全局作用域中声明的变量和函数会作为window的对象直接调用。

2.函数作用域

(1)调用函数时,函数作用域被创建,函数执行完毕,函数作用域销毁
(2)没调用一次就会创建一个新的作用域,他们之间是相护独立的
(3)在函数作用域中可以访问到全局作用域,函数外无法访问到作用域的变量
(4)函数作用域中访问变量,函数时,会先在自身作用域中寻找,没有找到则会到函数的上一级作用域中寻找一值找到为止

块级作用域

最外层函数和最外层函数外面定义的变量拥有全局作用域
所有未定义直接复制的变量自动声明为全局作用域
所有window对象的属性都有全局作用域
全局作用域有很大的弊端,过多的全局作用域会污染全局命名空间

作用域的深层次理解

执行期的上下文
- 当函数代码执行前期会创建一个上下文的内部对象AO(作用域)
- 这个内部对象是预编译时候创建出来的,因为当函数被调用的时候会先进行预编译
- 在全局代码执行的前期会创建一个执行期的上下文GO
- tips:函数只有在执行的时候才有自己的AO

函数作用域预编译

1.创建AO对象AO{}
2.找到形参和变量声明,将变量声明和形参名当作AO对象的属性名值为undefined
3.实参形参相统一
4.找函数声明 如果函数声明和变量声明名称一致的话会覆盖变量的声明

全局作用域的预编译

1.创建GO对象
2.找变量声明,将变量名最为对象的属性名值为undefined
3.找函数声明值赋予函数体

作用域链

有了作用域就会有作用域链
定义:
在自己的作用域内没有找到该变量,就去父级作用域查找,依次向上查找直到window对象被终止
作用域链的作用:
是保证执行环境有权访问所有变量和函数的有序访问,通过作用域链,可以访问到外层函数和变量、
实质:
是一个指向变量对象的指针列表,变量对象是一个包含了执行环境中所以变量和函数的对象。作用域链的前端是当前执行的上下文的变量对象。
作用域链会保存到一个隐式的属性中去[[scope]]这个属性是我们用户找不到的,但是是存在的,让js引擎来访问的里面存储的就是作用域链。作用域分别是AO和GO 作用域链是AO和GO的集合
[[scope]] 放的是作用域链的集合


function fn(){}
console.log(fn.name) //fn 这种属性是用户可以访问到的,但是有的属性确实访问不到比如[[scope]],它里面存放的就是作用域链。作用域分别是AO GO 作用域链是AO和GO的集合

//代码1
var global
function a(){
	//a函数执行进到这里
	function b(){
		var bb = 123
		aa = 0
	}
	var aa = 123
	b()
}
a()

当a函数定义,他所在的环境所看到的是这样。作用域链最顶端预编译的时候产生了一个GO GO里面有一个变量叫global 和一个a:function (如下图)

//a定义所看到的
var global 
function a(){}
a()

在这里插入图片描述
继续往下看上面(代码1)当a定义完后,只有a执行b函数才能定义,当a函数执行的时候(如下图)它的作用域链的首位放的是它自己的AO a函数执行后开始函数的预编译,函数的预编译的最顶端放的就是自己的AO,作用域的第二位放的是GO。
当a执行的时候作用域链的最顶端一定是有关自己的AO,第二位放的是外面的GO。这些都是在预编译的时候产生的[[scope]]里存放的就是scope chain (就是作用域链)它的首位放的是自己的AO
在这里插入图片描述
继续往下看代码1a执行完后b执行开始定义,如下面代码。b函数的时候它能看到的事件和a执行的时候看到的是一样的如图b1
当b函数执行的时候作用域链是这样的。**(看图b2)它的首位一定是b函数自己的AO,然后看到的是外部a函数的AO之后看到的是最外层的GO。
函数执行完毕后要做销毁,要斩断作用域链,作用域链销毁
(看图b3)**下次重新执行线在连上

var global
function a(){
	function b(){}
	var aa = 234
	b()
}
a()

图b1
在这里插入图片描述
图b2
在这里插入图片描述
图b3
在这里插入图片描述

闭包

定义: 指的是有权访问另一个函数作用域中变量的函数,常见的创建闭包的方式是,在函数内创建一个新函数,新函数可以访问到函数的局部变量
闭包有两个常用的用途:
一个是让我们在函数外包能够访问到函数内部的变量。通过闭包,可以通过在外边调用闭包函数,从而在外部访问到函数内部的变量,通过这个方法来创建私有变量
另外一个是在已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收

为什么js会产生闭包

function a(){
	var a = 123;
	function b(){
		var b = 456
		console.log(a);//123
	}
	return b
}
let res = a()
res()

这个闭包用图表示
在这里插入图片描述
闭包怎么形成的?
a函数执行完毕销毁只是关闭了自己的作用域链(图a),b函数没有被销毁,b函数被保存到了外部执行,虽然在外部但是由于在预编译的时候可以拿到aa的变量,并且链没有销毁(图b),所以外部执行的时候可以拿到aa变量。闭包就是这样产生的。产生闭包就是js的特性导致的
tip:b函数被保存到了外部为什么依然可以拿到a函数执行的a变量呢
(在它出身的时候就可以当问a的AO)

图a
在这里插入图片描述
图b
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值