在深入闭包的底层原理之前,先深入JS作用域
AO
AO的步骤:
- 再函数被调用之前,创建AO(Activation Object)对象,又叫执行期上下文;(上下文是唯一的,一个函数被多次被调用时会创建多个上下文)
- 寻找形式参数和变量声明作为AO的属性名,并赋值为undefined;
- 传入实际参数的值;
- 在函数体内寻找函数声明,放入作为AO的属性,并赋值为其函数体。
先看一个阿里面试题的例子:
function test(a,b){
console.log(a)
function a(){};
console.log(a)
console.log(c)
console.log(b)
var b = function(){}
console.log(b)
function c(){}
}
test(1,2)
//解析
//在test被调用之前,会创建执行上下文 AO:{};寻找形式参数和变量声明作为AO的属性名,并赋值为undefined
AO{
a:undefined
b:undefined
c:undefined
}
//首先看a : 在test(1,2)传入实际参数后 可以知道 a被赋值1 ===> a:1
//然后再函数体内寻找函数声明 * 函数声明一定要以函数function开头 不是以function的函数不能作为声明函数 test函数中存在 "function a(){}" 给a赋予函数体
// 此时 a:function(){}
test(1,2) 控制台分别输出
ƒ a(){}
ƒ a(){}
ƒ c(){}
2
ƒ (){}
1.2 Go
GO GO步骤:
创建GO(Global Object)对象; 寻找变量声明作为GO的属性名,并赋值为undefined;
寻找函数声明,放入作为GO的属性,并赋值为其函数体。
----------------------------------------------------同AO一样----------------------------------------------------
1.3 scope
【scope】:作用域链,是 [AO+Go]的集合
运行期上下文:当函数执行时,会创建一个名为执行期上下文的内部对象,它定义了一个函数执行时的环境。
函数每次执行时其上下文是唯一的,多次调用一个函数会生成多个执行期上下文,当函数调用完,其对应的执行期上下文被销毁。
查找变量时则从作用域的顶端开始查找。
2.闭包底层原理
2.1了解闭包底层原理+scope chain
function a(){
var aa=123
function b(){
var bb=234
console.log(aa)
}
return b
}
var test = a()
test()
//我们知道 这个test()会在控制台输出 123,为什么b被保存到外部之后,还能够拿到aa的值呢
//首先在function a执行之前 会创建一个作用域链[[scope]],[[scope]]里面包含了[scope chain] ,scope chain就是AO+GO的集合
首先在a()执行之前会产生如图所示的作用域链
在a函数执行之前,a预编译创建的AO一定是放在 [scope chain] 的首端,AO里面就是存放着的a的参数和变量声明
当 b() 定义的时候,看到的世界其实是和a函数一样,其实,b函数的出生 就是站在a函数的肩膀上的
a执行才让b定义,所以a和b所看到的世界都是一样的,scope chain可以看做如下形式;
上面的示例中,b被return到外部 ;当a执行完毕,销毁scope chain 的时候 ;b执行,既然a和b所看到的世界都是一样的,那当a的scope chain 销毁的的时候 ,执行b,b看到的世界和a一样,可以知道 即使a被销毁了,b依旧能够访问到aa变量 ; 那么这就是闭包形成的底层原理!!!!
2.2闭包经典应用+节流防抖案例
##防抖案例
function debounce(fun,delay){
let timer
return function(args){
clearInterval(timer)
timer = setTimeout(function(){
fun(args)
},delay)
}
}
// debounce返回了一个函数保存在外部
function inputFun(a){
console.log(a)
}
const debounceInput = debounce(inputFun,1000)
let a=111
debounceInput(a)
##节流
function throttle(func,wait){
let timeOut
return function(){
if(!timeOut){
timeOut = setTimeout(
function(){
timeOut = null
func()
},
wait
)
}
}
}
//func为要执行的函数,wait 是等待时长