闭包

什么是闭包?

函数与对其状态即词法环境(lexical environment)的引用共同构成闭包
也就是说,闭包可以让你从内部函数访问外部函数作用域
标题在JavaScript,函数在每次创建时生成闭包

闭包的特点

1. 在一个函数内部定义另外一个函数,并且返回内部函数或者立即执行内部函数。

2. 内部函数可以读取外部函数定义的局部变量

3. 让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生环境一直存在。

变量作用域

要理解闭包,首先要理解javascript的特殊的变量作用域。

变量的作用域无非就两种:全局变量和局部变量

javascript语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量

注意点:如果声明的变量没有使用var命令去声明,那么即使你是在函数内部去声明的局部变量,实际上却是全局变量。

闭包的写法和用法

常见例子

function f1(){
			var num="hello world!";
			function f2(){
      		consloe.log(num);
       }
    	    return f2;
}
var nav = f1();
nav();

在这个例子中,nav是执行 f1函数 时创建的 f2 函数实例的引用,而 f2 实例仍可访问其词法作用域中的变量,即可以访问到 num 。由此,当 nav 被调用时,num仍可被访问,其num变量值 hello world! 就可以在控制台输出。

这就是Javascript语言特有的 “链式作用域” 结构。

思考
在学习js的时候,或者面试的时候,会经常碰到这一道经典题目:

for(var i = 0; i < 3; i++) {
	 setTimeout(function () {
    	console.log(i);
	});
}
console.log('hello world!');

很多初学者,可能会以为答案会是:

0
1
2
hello world!

但仔细研究,你会发现这道题答案错误,且涉及了异步、作用域、闭包。

首先,我们先分析for循环为什么不是012,而是333。因为在所有同步代码执行完毕之后,for循环里的i值早已变成了3,循环已经结束。(注意,for循环的圆括号部分也是同步代码)
那么,为什么hello world!会先输出呢?其实定时器并不是同步的,它会自动插入任务队列,等待当前文件的所有同步代码和当前任务队列里的已有事件全部运行完毕后才能执行

所以最后的答案便是:

hello world!
3
3
3

既然我们了解了这道题的原理,那么我们来拓展一下:for循环里面包着一个定时器并按顺序打印出hello world! 123。这里可能要思考了,因为同步代码的问题,使用简单的函数是无法得出我们需要的值。这里,就要说到闭包的另外一种函数形式了---------- 匿名自执行函数

通常的写法为:

(function(window,undefined){
	//方法内容
})(window)

分析结构:
  (1)其中后一个 “window”为全局window对象,是要往这个函数中传入的对象。,前一个window为函数的形参,接收传入的window对象,而undefined参数的加入,是因为undefined在老一辈的浏览器是不被支持的,直接使用会报错,js框架要考虑到兼容性,因此增加一个形参undefined。
  (2)为什么要传入window对象呢,因为这个匿名自执行函数如果多次用到全局window对象,将全局window对象直接传入函数中去作为局部变量可以提高js的性能,减少作用域查询的时间。

所以解决这道题的写法为:

for(var i = 0; i < 3; i++) {
    (function(i){
        setTimeout(function () {
    		console.log(i);
		});
    })(i);
}
console.log('hello world!');

使用()()匿名自执行函数的好处

1.当你编写插件时,插件的变量名方法名,都不会与其他js方法冲突
2.你可以将匿名自执行函数单独写在一个js文件中,可以像使用dll,jar包等一样去调用它。

闭包的优缺点

闭包的优点
  可以重复使用变量,并且不会造成变量污染,可以用来定义私有属性和私有方法

闭包的缺点
  1.闭包使函数内部的变量不能被内存释放,这些变量就会占用内存,内存消耗大,可能会导致内存泄露。
  2.解决这个问题的办法就是在不使用这些变量时,及时把不需要的局部变量全部删除。

使用闭包的注意点

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值