关于对闭包的理解以及实例

闭包,在面试时经常被问到,也看了很多文章,书籍,但是对于闭包的理解还是停留在“在一个函数内创建另一个函数,以此可以访问使用函数中的变量”,在这总结一下加深理解。
首先,理解闭包得先了解作用域链。

1、执行环境及作用域

代码在某个环境中执行时,会创建变量对象的一个作用域链

eg:环境是函数,变量对象就是其**活动对象*,变量对象查找:
在这里插入图片描述

2、闭包

例:

function a() { 
	var i = 0; 
	function b() { alert(++i); } 
	return b; 
} 
a()() // 1
a()() // 1
var c = a(); // 变量c实际上是指向了函数b
c(); // 1 再调用c()相当于执行b()
c(); // 2

这就创建了一个闭包,为啥?因为函数a外的变量c,引用了函数a内的函数b,函数b使用了函数外的变量i;
本来局部变量i会随着函数调用的结束而被销毁,但是,执行c = a()时,c返回了一个匿名函数的引用,可访问a被调用时产生的环境,i被保留。

闭包和面向对象设计

闭包以方法的形式包含了过程,闭包则是在过程中以环境的形式包含了数据

来两个对比吧,以第一个例子为例,换成面向对象的写法:

	var func = {
		a: 1,
		call: function(){
			++this.a;
			console.log(this.a)
		}
	}
	func.call() // 2
	func.call() // 3
	//或者
	var func = function(){
		this.a = 1
	}
	func.prototype.call = function(){
		++this.a;
		console.log(this.a)
	}
	var f = new func()
	f.call()
	f.call()
闭包与内存

使用闭包会增大内存的使用量过度使用闭包也会降低脚本性能
看到很多说乱用闭包会造成内存泄漏。局部变量被封闭在闭包形成的环境中,不能被销毁,这是有些违背设计,但是,使用闭包,我们是主动的把变量封闭在闭包中的,在之后需要回收变量,可以手动把变量设置为null。

使用闭包比较容易形成循环引用,如果闭包的作用域链中保存一些DOM节点,就可能造成内存泄漏
因为IE下使用引用计数的垃圾回收机制,如果两个对象形成循环引用,两对象都无法被回收。
解决:把循环引用中的变量设为null

function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      aler(el.id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
}

// 改成下面
function  showId() {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      aler(id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
    el = null    // 主动释放el
}

3、替换闭包使用

比较常见的场景有,用for绑定事件,

4、闭包实例

1、调用对象方法,返回匿名函数,与this相关

在看闭包的问题时,有看到这个例子,作为object调用,但是返回的是全局里的变量,这属于this指向问题,在之前写的this相关文章里有提到,作为对象的方法被调用,若方法返回匿名函数,内部函数也就是返回的匿名函数,this是指向全局变量的。匿名函数的执行环境是全局的。

内部函数的this就是全局变量window

	var name = "The Window";   
	var object = {   
		name : "My Object",   
		getNameFunc : function(){   
			//	var self = this 想要输出My Object
			return function(){   
	        return this.name;   
	     };   
		}   
	};   
	alert(object.getNameFunc()());  // The Window

如果在getNameFunc函数里定义一个name变量,输出的是什么。
在向上查找name时,getNameFunc中存在name变量,随即返回。

	getNameFunc : function(){ 
		var name = "inner name"  
		return function(){   
        return this.name;    // inner name
     };   
	} 
2、关于settimeout的实例

这种问题无需使用闭包来消耗性能,使用let替换var,形成一个块作用域

	for (var i = 1; i <= 5; i++) {
	  setTimeout( function timer() {
	      console.log(i);
	  }, 1000 );
	} // 5个6(每隔1秒输出6)
3、较复杂实例

在找闭包实例时看到这个例子,是作者拿着面试的,很有意思(作者链接

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,?,?,?

答案:
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
PS:
参考文章:js设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值