闭包的理解

首先需要了解什么是作用域和作用域链?

作用域(scope)

1、什么是作用域?

一个变量的使用范围叫做作用域

2、为什么存在作用域?

为了避免函数内外的变量间互相干扰

3、作用域分为两种

    1、全局作用域:保存着所有全局变量/函数

    2、函数作用域:保存函数内的局部变量

4、变量的使用顺序

先使用局部变量,当找不到局部变量时,再去全局寻找

5、函数的生命周期**

1、程序执行前

    创建一个数组(执行环境栈ESC):用于记录正在执行的函数

    浏览器本身也是一个程序,会创建全局变量作用域对象(window),保存所有浏览器内置的对象和方法

    

2、函数定义时

    在全局创建函数名变量

    在window之外创建函数对象保存函数定义

    函数名变量通过地址引用函数对象

    函数对象使用scope属性指回自己来自的作用域


3、函数调用时

    现在ECS中添加本次函数调用的记录

    为本次函数调用创建专门的函数作用域的对象(AO)

    在函数作用域对象中保存本次函数调用所需的所有局部变量

    函数作用域对象的parent属性指向函数来自的父级作用域对象

    变量的使用顺序:就近原则->先使用局部,如果局部没有再去全局window中找


4、函数调用后

    将本次函数调用的记录从ECS中出栈

    导致函数作用域对象释放

    导致局部变量一同释放

    故:局部变量不可重用!


作用域链

由多级作用域主机引用而形成的链式结构

作用:

    -1、保存了所有变量

    -2、控制了变量的使用顺序:先局部,后全局


1、什么是闭包?

即重用变量,又保护变量不被篡改的一种机制

2、为什么要使用闭包?

全局变量-优点:可以重用。                         缺点:随处可用,容易被污染

局部变量-优点:仅函数内可用,不会被污染。缺点:不可以重用

当我们既想重用变量又想保护变量不被污染的情况下,就可以使用闭包。

3、判断闭包的三个特点***

1、用外层函数包裹受保护的变量和内层函数

2、外层函数将内层函数对象返回到外部(共3种途径)

    -(1)使用return

    -(2)直接给全局变量赋值

    -(3)将内层函数包裹在数组/对象中返回

3、使用者调用外层函数,获得内层函数的对象

4、闭包是怎么形成的?

外层函数的函数作用域对象(AO)无法释放

5、闭包的笔试题答题技巧

可以用两步来判断是否是闭包:***

    1、找受保护的变量:外层函数的局部变量

    2、找外层函数共返回哪些内层函数:一次外层函数的调用,返回的多个内层函数,公用同一个闭包中的受保护的变量。

6、闭包会导致的问题

内层函数比普通函数占用更多的内存空间(外层函数的AO)

如何解决?

一旦闭包不再使用时,应立即释放闭包结构

例:函数 = null

闭包的一些例子

1、使用闭包实现点赞功能

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
 </head>
 <body>
  <button id="btn_zan" οnclick="zan()">赞(+0)</button>
	<script>
		var zan=(function(){        //使用匿名函数
			var i=0;            //要保护变量i
			return function(){  //使用内部函数返回,可以实现闭包
				i++;        //让i每点击一次递增
				btn_zan.innerHTML="赞(+"+i+")";//输出赞的次数
			}
		})();
	</script>
 </body>
</html>

原理:如果单在函数内定义i,例如:

<script>
var zan = function(){
            var i =0;
            i++;
            btn_zan.innerHTML="赞("+i+")";  
          }
</script>

因为这里的i是局部变量,不可重用,所以i只会被递增一次,故i一直为1

而如果将i定义为全局变量,例如:

<script>
var i =0;
var zan = function(){ 
            i++;
            btn_zan.innerHTML="赞("+i+")";  
          }
</script>

这样的话确实可以实现i的点击累加,但是由于i在这里是全局变量,故容易被污染,如果在函数任意位置重新定义了i,则函数无法

执行,所以这个时候需要使用闭包来实现既能重用又不会被污染

2、经典笔试题

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
 </head>
 <body>
  <script>
		function fun(){
			//1. 找受保护的变量: 外层函数的局部变量
			var n=999;
			//2. 外层函数共抛出哪些内层函数
			nAdd=function(){n++};
			return function(){ console.log(n) };
		}
		var get=fun();//fun的AO(n=999)
		//var nAdd: function(){n++}
		//get: function(){ console.log(n) };

		get();//999
		nAdd();//AO(n=1000)
		get();//1000
	</script>
 </body>
</html>

这个例子参照判断闭包的三个特点中:外层函数将内层函数对象返回到外部的三种途径中的第二个途径-直接给全局变量赋值

因为nAdd没有被定义过,强行赋值会使JS将nAdd定义为全局变量,所以他也会被返回到外部,故也将执行

所以当先调用一次get()的时候函数作用域对象中的n为999

再调用nAdd()时,由于返回到外部,所以也会对函数作用域对象造成影响,所以这个时候n变成了1000

当再次调用get()时,函数作用域对象中的n变成了1000,所以会输出1000


3、经典笔试题2:

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
 </head>
 <body>
  <script>
		function fun(){
			//1. 找受保护的变量: i=0
			for(var i=0,arr=[];i<3;i++){
				arr[i]=function(){ console.log(i);  }
			}//*************i=3******************
			//2. 外层函数向外抛出了哪些内层函数: 3个
			return arr;
		}
		var funs=fun();
		//funs:arr:[
//				function(){ console.log(i); },
//				function(){ console.log(i); },
//				function(){ console.log(i); }
		//]
		funs[0]();//3
		funs[1]();//3
		funs[2]();//3
	</script>
 </body>
</html>

这个例子中,数组容易迷惑人

这个例子参照判断闭包的三个特点中:外层函数将内层函数对象返回到外部的三种途径中的第二个途径-将内层函数包裹在数组/对象中返回。

所以即便是数组也返回了外部

第二个迷惑点是

arr[i]=function(){ console.log(i); }

这里虽然看起来i都受到for循环的影响,其实不然,因为function()的意思是构造一个函数

在后面function(){console.log(i);}中,只是构造了函数,并没有调用函数,所以function中的i并不受for的影响

所以只有arr[i]中的i会跟着for进行改变,并将function(){console.log(i);}添加到数组中,所以最后数组arr中

会包含三个function(){console.log(i);}

arr[

function(){console.log(i);}, -对应下标0    因为for循环从0开始 

function(){console.log(i);}, -对应下标1    到这里i变成1所以下标为1

function(){console.log(i);}  -对应下标2    到这里i变成2所以下标为2

]

另外:

for(var i=0,arr[];i<3;i++)

i在循环结束时并不等于2,因为等于二的情况下还是小于3的,只有再进入一次循环,执行一次i++时才算循环结束,

因为当i真正等于3的时候,才真正的不满足循环条件,循环才算真正结束,所以这个时候函数作用域对象中的i=3


未完待续。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值