【JS】JavaScript之闭包的详细解释

前言

闭包在js中应用非常广泛,也是极为重要的知识点。刚开始了解闭包时也是云里雾里,花了好长时间才搞明白。下面来详细叙说一下闭包的知识。

一、准备知识

要理解好闭包的概念,我们首先得清楚下面的三个知识点:

  • 作用域(全局变量和局部变量)
  • 链式作用域结构
  • js垃圾回收机制

1、作用域
变量作用域分为全局变量局部变量
先看下面一段js代码

    var name = '小明'
    function man(){
	     console.log(name)
		 var age = '20'
	}
	man()
	console.log(age)

输出的结果:
man()输出小明console.log(age)报错age is not defined

上述代码中name全局作用域下,即为全局变量。age在函数作用域下,即局部变量。而JavaScript的运行机制为函数内部可以访问外部的全局变量,反之,函数外部无法读取函数内部的局部变量

2、链式作用域结构
那如何得到上图中age的值呢,尝试在man()函数里面添加一个函数,如下:

	var name = '小明'
	function man(){
		console.log(name)
		var age = '20';
		(function getage(){
			console.log(age)
		}())
	}
	man()

结果:第6行输出:20
getage函数存在于man函数的内部,age变量为局部变量。前面作用域的例子说明了man函数外部不可以访问age,但是man函数变量对getage函数是可见的。当前反之,getage里的变量man无法访问。这就是js的链式作用域结构

概括一下上面的话:首先子对象会在自身的作用域寻找变量,如果没有则一层一层的向父级寻找。所以,父级作用域的变量对子作用域可见,反之不可见

3、垃圾回收机制
垃圾回收机制的知识点很多,下面简单的谈一谈。

  • 变量生命周期
    一个变量不再被使用,即生命周期结束,它所指向的内存会被垃圾回收机制收回。全局变量在浏览器关闭时结束生命,局部变量在函数执行完毕时结束。

  • 回收机制
    (1)如果一个对象不再被引用,那么它指向的内存会被收回。
    (2)如果两个对象互相引用,不在被第三个引用也会被回收
    (3)如果两个对象互相引用,其中一个别第三方引用,那么就不会回收这些变量。

二、什么是闭包

前面说链式作用域的时候,子函数getage可以访问到父函数man里的局部变量。

那么如何让man函数外部访问man函数的局部变量呢?
只要将子函数getage作为父函数man的返回值,那么就可以在man的外部访问到了。
代码如下:

	function man(){
		var age = 20
		function getage(){
			console.log(age)
		}
		 return getage
	}
	var showage = man()
	showage()

结果:showage()执行之后输出age
那么这段代码有什么特点呢?

  • 函数a包含函数b
  • 函数a返回函数b
  • 函数b在函数a外部被引用

对于上面的代码,getage即为一个闭包
闭包概念: 闭包是一个可以读取函数内部局部变量的函数

也就是说,当函数a内部创建的函数b被外部引用时,就创建了闭包b

三、闭包的作用

1、访问局部变量

因为函数内部的变量为局部变量,外部无法使用。当需要将变量提供给外部时使用闭包方式。

	function man(){
		var age = 20
		function getage(){
			console.log(age)
		}
		 return getage
	}
	var showage = man()
	showage()//20

2、保存局部变量

前面说到,因为垃圾回收机制的存在,函数执行完成后里面的局部变量会被销毁,但有时我们希望可以被外面访问到并且保存每一次的执行结果

例子:

	function num(){
		var num = 0
		return function addnum(){
			console.log(++num)
		}
		
	}
	var changnum = num()
	changnum()//1
	changnum()//2
	changnum()//3

这里局部变量num始终保存在内存中,每一次调用都在上一次的基础上加1。

3、实现类的封装

属性为私有属性外部不可访问,通过闭包将访问属性、修改属性的方法提供给外部使用。

   function Man(){
	   var name = '小明'
	   var age = 20
	   return {
			showname:function (){
				return  name
			},
			changename:function (newname){
				name = newname
			}
		}
   }	
   var m = new Man()
   console.log(m.showname())//小明
   m.changename('小黑')
   console.log(m.showname())//小黑

四、闭包的注意点

1、内存泄漏

  • 原因:正常情况下,因为垃圾回收机制,局部变量在函数执行完毕之后其生命周期也结束,所以内存会被回收释放。但是因为我们使用了闭包,导致变量一直保存在内存,无法得到释放,造成内存的消耗。
  • 解决:退出函数前,将不使用的局部变量删除,或者解除函数的引用

示例:

	function num(){
		var num = 0
		return function addnum(){
			console.log(++num)		
		}
	}
	var changnum = num()
	changnum()//1
	changnum()//2
	changnum()//3
	changnum = null//解除num()的引用,此时变量num释放

2、谨慎修改变量
因为通过闭包你可以对里面的局部变量进行修改,如果通过闭包去实现一个类,那么可以通过类公有的方法去操作私有属性,这里需要注意使用。

五、总结

闭包在JavaScript使用非常广泛,也是重要的内容,高级应用的必备。

你可能感兴趣的:(JavaScript,javascript)