闭包的底层原理
闭包的底层原理其实就是作用域链被保存下来的原因,具体往下看
作用域链
- 之前我们讲作用域和预编译的时候,函数作用域会创建AO对象,全局作用域会创建GO对象,它们会被保存到一个隐式的属性[[scope]] 中去,这个属性是我们访问不到的,但是是的的确确存在的熟悉,是给js引擎访问的,里面存储的是AO和GO的集合
要点:
- 函数定义的时候会形成作用域链,但是函数执行完就会断开作用域链
- 外层函数的执行和其内层函数的定义的作用域链是相同的(精髓点,当外执行完成后销毁,内层定义其实还是保存着这条作用域链的,所以仍能访问相应的变量)
var global;
function a() {
function b() {
var bb = 123;
aa = 0;
}
var aa = 123;
b();
}
a() //第一个a函数
a() //第二个a函数
a定义
第一个a函数执行前会先定义,定义的时候会产生它的作用域链,因为是全局函数,所以会产生GO,可以看出只有global和a函数;
a执行当a函数执行的时候会产生它自己的AO对象(函数作用域),如图,aa变量和b函数;
以上讲述了函数中作用域链的产生方式;注意当第二个a函数执行他会有他自己的作用域链,它们之间是互不影响的!
下面我们通过以下函数说明闭包的原理:
function a() {
var aa = 123;
function b() {
var bb = 234;
console.log(aa)
}
return b;
}
var res = a();
res();
Scope1.png
Scope2.png
Scope3.png
就看图1和2就可以,a执行和b定义的作用域链是一样的,a执行完会断开自己的作用域链,但是此时b定义的作用域链仍存在;闭包返回的是函数,此时还未执行,属于定义阶段,所以可以访问作用域链上的变量
闭包的运用
- 单例模式
当多个地方调用同一个能力模块,我们只需要产生一个实例即可。(例如多个地方需要弹出弹框,实际页面只需要产出同一个弹窗即可)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="loginBtn">登录</button>
</body>
<script>
let createLogin = function () {
let div = document.createElement("div");
div.innerHTML = "这是登录弹窗";
div.style.display = "none";
document.body.appendChild(div);
return div;
}
let getSingle = function (fn) {
var result;
return function () {
return result || ( result = fn.apply(this, arguments) )
}
}
let create = getSingle(createLogin);
document.getElementById("loginBtn").onclick = function () {
let loginLay = create();
loginLay.style.display = "block";
}
</script>
</html>
网友评论