笔记-JS 闭包

闭包定义

从严格意义上来说,构成闭包需要有对上下文词法作用域中变量的引用,并在外部函数执行完毕时,被引用的变量并不会被垃圾回收器回收。

函数可以访问函数内部的变量

function myFunction(){
	var a=4;
	return a;
}

函数也可以访问函数外部的变量

var a=4;
function myFunction(){
	return a;
}

javascript查找变量即变量解析的过程,首先在当前定义的局部作用域中查找,如果未发现,就会查找上一层作用域。如下,变量scope被定义为outer函数的局部变量,而inner函数定义时即确定了其作用域,并引用了外部函数的局部变量scope,所以输出结果为20而不是110.

inner函数作为outer函数的执行结果被返回,当outer函数在执行完毕后,定义在其内部的变量scope并没有被返回,而是通过函数fn的执行被获取,这里的inner函数即形成了闭包。

function outer(){
	var scope=10;
	return function inner(){
		scope+=10;
		console.log(scope);
	}
}
var scope=100;
var fn=outer();
fn();		//输出结果:20
fn();	//试着再次调用fn,此时输出结果为30

函数作为参数传递给自执行函数。

var scope=10;
function calculate(addend){console.log(scope+addend);}
(function(ca){
	var scope=100;
	ca(5);		//输出结果:15
})(calculate);
一些例子

bar执行时,访问了foo内部的变量a和b,闭包产生。

//demo1
function foo(){
	var a=20;
	var b=30;
	function bar(){
		return a+b;
	}
	return bar;
}
var bar=foo();
bar();

foo中定义的函数在执行时访问了foo中的变量,闭包产生。

//demo2
function foo(){
	var a=20;
	var b=30;
	function bar(){
		return a+b;
	}
	bar();
}
foo();

内部函数_add被调用执行时,访问了add函数变量对象中的x,闭包产生。

//demo3
function add(x){
	return function _add(y){
		return x+y;
	}
}
add(2)(3);	//5

getName在执行时,它的this指向的是window对象,这时没有形成闭包的环境,因此没有闭包产生。

//demo4
var name="window";
var p={
	name:"peter",
	getName:function(){
		return function(){
			return this.name;
		}
	}
}
var getName=p.getName();
var _name=getName();
console.log(_name);

对demo4的改动1

//demo5
var name="window";
var p={
	name:"peter",
	getName:function(){
		return function(){
			return this.name;
		}
	}
}
var getName=p.getName;
//利用call使得this指向p对象
var _name=getName.call(p);
console.log(_name);

对demo4的改动2

//demo6
var name="window";
var p={
	name:"peter",
	getName:function(){
		//利用变量保存的方式保证其访问的是p对象
		var self=this;
		return function(){
			return self.name;
		}
	}
}
var getName=p.getName();
var _name=getName();
console.log(_name);
垃圾回收机制
  • 当一个函数的执行上下文运行完毕之后,内部的所有内容都会失去引用而被垃圾回收机制回收。
  • 闭包的本质就是在函数外部保持了内部变量的引用,因此闭包会阻止垃圾回收机制进行回收。
function f1(){
	var n=999;
	nAdd=function(){
		n += 1;
	}
	return function f2(){
		console.log(n);
	}
}
var result=f1();
result();	//999
nAdd();
result();	//1000

nAdd与f2都访问了f1中的n,因此它们都与f1形成了闭包。变量n的引用被保留下来,因此nAdd每运行一次n加1。

应用闭包
循环、setTimeout与闭包
//运用闭包的知识,修改这段代码,使代码的执行结果为隔秒输出1,2,3,4,5
for(var i=1;i<=5;i++){
	setTimeout(function timer(){
		console.log(i);
	},i*1000);
}

setTimeout的第二个参数访问的都是当前的i值,因此第二个i值分别是1,2,3,4,5。而第一个参数timer函数中虽然访问的是同一个i值,但是由于延迟的原因,当timer函数被setTimeout运行时,循环已经结束,即i变成了6。因此,这段代码的直接运行结果是隔秒输出6。

使用匿名函数更改。

//demo1
for(var i=1;i<=5;i++){
	(function(i){
		setTimeout(function timer(){
			console.log(i);
		},i*1000);
	})(i);
}

//demo2
for(var i=1;i<=5;i++){
	setTimerout((function(i){
		return function timer(){
			console.log(i);
		}
	})(i),i*1000);
}
单例模式与闭包

最简单的单例模式。

var per={
	name:"jake",
	age:20,
	getName:function(){
		return this.name;
	},
	getAge:function(){
		return this.age;
	}
}

上续单例模式的属性可以被外部更改,在很多场景中,我们期望对象能够有自己的私有方法与属性。

有私有方法与属性的单例模式。

var per=(function(){
	var name="jake";
	var age=20;
	return{
		getName:function(){
			return name;
		},
		getAge:function(){
			return age;
		}
	}
})();
per.getName();

私有变量的好处在于,外界对于私有变量能够进行什么样的操作是可以控制的。如上,我们已经利用闭包解决问题了,匿名函数中,当name被getName访问时,闭包产生,因此A中两个属性都会被保留下来。

调用时才初始化的单例模式

var per=(function(){
	//定义一个变量,用来保存实例
	var instance=null;
	var name="jake";
	var age=20;
	//初始化方法
	function initial(){
		return{
			getName:function(){return name;},
			getAge:function(){return name;}
		}
	}
	return{
		getInstance:function(){
			if(!instance){
				instance=initial();
			}
			return instance;
		}
	}
})();
var p1=per.getInstance();
var p2=per.getInstance();
console.log(p1===p2);	//true

我们在匿名函数中定义了一个instance变量用来保存实例。在getInstance方法中判断是否对它进行重新赋值,因此变量instance仅仅只在第一次调用getInstance方法时赋值了,符合单例模式的思路。

模块化与闭包
其他应用

利用闭包保存中间计算结果,实现计算结果的缓存。如下利用闭包函数计算斐波那契数列。

var count=0,
fib=(function(){
	var arr=[0,1,1];	//前三位直接返回
	return function(n){
		count++;
		var res=arr[n];
		if(res){
			return res;
		}else{
			arr[n]=fib(n-1)+fib(n-2);
			return arr[n];
		}
	}
})();
fib(10);
滥用闭包的危害

滥用闭包会导致诸如内存泄漏的性能问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值