signed

QiShunwang

“诚信为本、客户至上”

我对闭包的理解

2021/6/3 16:14:28   来源:

1、什么是闭包?

闭包就是可以访问其他函数内部变量的函数,举个例子,一个内部函数总是可以访问其所在的父函数中声明的参数和变量,那么这个内部函数就是闭包。

2、闭包的举例

例1:

function pp() {
			return function sum() {
				let m=1;
				return function show() {
					console.log('m:'+ ++m);
				};
			};
		}
let a=pp()()
console.log(a());//2
console.log(a());//3
console.log(a());//4
console.log(a());//5

这里,局部变量m会常驻在内存中,从而打印 2,3,4,5;

例2:

定时器与闭包

for(var i=1;i<=3;i++) {
			setTimeout(function () {
				console.log(i);
			},1000);
		}//4  4  4

这里,为什么打印4,4,4,而不是1,2,3 ???

因为setTimeout是异步执行,1秒后往任务队列里面添加一个任务,只有主线上的全部执行完,才会执行任务队列里的任务,var是全局作用域,当主线执行完成后,i是4,所以打印4,又因为每一次for循环的时候,setTimeout都执行一次,所以for循环3次,打印3个4。

我想让它打印1,2,3,有什么方法吗?

法(1):

for(let i=1;i<=3;i++) {
			setTimeout(function () {
				console.log(i);
			},1000);
		}//1 2 3

只是将var 变成了let,通过使用 let 来声明块变量,这时候变量就能作用于这个块,所以 function就能使用 i 这个变量了。

法(2):

闭包作为参数传递

for(var i=1;i<=3;i++) {
			(function(a) {
				setTimeout(function () {
				console.log(a);
			},1000);
			})(i);
	}//1 2 3

这里给function变成立即执行函数(声明一个函数,并马上调用这个匿名函数就叫做立即执行函数;)立即执行函数内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

例3:

返回年龄在10-20之间的数组(filter函数)

		let arr = [
			{
				name: 'zs',
				age: 5
			},
			{
				name: 'ls',
				age: 16
			},
			{
				name: 'ww',
				age: 20
			}
		
			]			
		function between(a,b) {
			return function(v) {
				return v.age>=a && v.age<=b;
			}
		}
		console.table(arr.filter(between(10,20)))

结果:
在这里插入图片描述

例4:

函数作为返回值

let pp = {
			user: "小明",
			get: function() {				
				return function() {
					return this.user;
				}
			}
		}
		let a=pp.get();
		console.log(a());//undefined

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。

这里this指向window,而此时window里面没有定义user。

怎样打印出“小明”呢???

法(1):

将this保存在一个变量that中 let that = this;

let pp = {
			user: "小明",
			get: function() {
				let that = this;
				return function() {
					return that.user;
				}
			}
		}
		let a=pp.get();
		console.log(a());   //小明

法(2):

箭头函数

let pp = {
			user: "小明",
			get: function() {
				//箭头函数
				return () => {
					return this.user;
				}
			}
		}
		let a=pp.get();
		console.log(a());   //小明

3、闭包的特点

  • 函数嵌套函数
  • 参数和变量不会被垃圾回收机制回收(局部变量会常驻在内存中)
  • 内部函数总是可以访问其所在的外部函数中声明的参数和变量

4、闭包的优点和缺点

  • 优点:变量长期驻扎在内存中,避免全局变量的污染。
  • 缺点:由于闭包携带包含它函数的作用域,因此比其他函数占用的内存更多,泄露内存。

5、使用注意点

(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。