javascript 高级系列之闭包(closure)
来源:互联网 发布:中国出口印度的数据 编辑:程序博客网 时间:2024/05/21 00:56
写在正文前:
写这篇文的时候,我也在思考,如何才能讲清楚闭包(closure)的概念,所以这两天我也一直没有写文,就怕写的不好,给初学者带来不好的理解;
如果这篇文有地方存在不足的,希望大家看的地方就指正一下,这样也是共同交流,共同进步;
进入正文 :
官方给的解释:
一个拥有许多变量和绑定了这些变量的环境的表达式
当初看到上面的解释的时候,我确实没有看懂上面意思,这太书面化了;
但从上面给出的定义,我们可以获取到两点信息:
- 变量
- 环境(其实就是作用域)
变量:从作用域来看:分为 全局变量 与 局部变量;
作用域:能够形成作用的, 一是整个js,全局作用域,一是花括弧{},当然像if for while这些条件语句形成的环境是不能造成作用域的;
通俗来说
- 从变量看:函数能够访问到函数外部的局部变量可以称为闭包;
从内存看:如果函数内部变量执行完之后不被GC回收即可称为闭包;
这里要额外说一下闭包的出现:为了避免出现定义全局变量,这样做有两个原因,一是因为全局变量不会被GC回收,一是防止全局变量造成变量污染;
为了能使函数能够访问到外部的局部变量;
其实闭包的出现确实有些矛盾之处,因为他出现的部分原因是为了解决全局变量不能被GC回收这样一个问题,但是最后发现很大一部分的闭包函数内部的变量也不能被GC回收;
所以我们使用闭包的时候一定要小心,不要过度的使用闭包,不然会使得IE的某些浏览器出现内存泄漏,造成过多的内存开销,最后造成浏览器崩溃的现象,如果必须使大量使用闭包,请手动删除不能被回收的变量;
接下来结合例子说明什么是闭包,看下面例子;
function parentFun(){ var a = 10; function childFun(){ var b = 20; var c = a + b; console.log(c); } childFun();}parentFun() // 30;
上面的例子是闭包吗?
很多人可能会说不是,因为从内存角度来看,变量a,在函数childFun执行完之后,就被销毁回收了,这能使函数parentFun形成闭包吗?
我的回答是能形成闭包,从变量作用域的变化来看,a是属于函数childFun外部函数定义的局部变量, 但是childFun却能访问并且使用到他,这就能使函数parentFun成为闭包;
当然观察角度不一样得到的结果就不一样,所以上面的例子要看你怎么去理解了,接下来看一个列子:
function parentFun(){ var a = 10; var b = 20; return function (){ b++; var c = a + b; console.log(c); }}var oParent = parentFun();oParent(); // 31;
上面的例子是闭包,相信所有人都会说是,因为这是最简单的典型闭包;
稍稍解释一下上面的例子,我们知道直接写函数名的时候只是重复写了一下函数而已,像上面的例子如果这样处理下:
console.log(parentFun) // ???//其实肯定是打印出函数本身;function parentFun(){ var a = 10; var b = 20; return function (){ b++; var c = a + b; console.log(c); }}
函数的调用在于后面的括号(),这才是对函数的调用,例如:parentFun();
如果写成下面这样相信更容易理解一点:
(function(){ var a = 10; var b = 20; return function (){ b++; var c = a + b; console.log(c); }})()
这是一个立即执行匿名函数,很明显函数结尾最后有个括弧,这就是对函数的调用了;
再来看第二个例子,函数内部返回如果是一个函数的话,那么返回的就是函数的本身,所以上面的例子调用的时候
var oParent = parentFun();//相当于下面这样;var oParent = function(){ b++; var c = a + b; console.log(c); }oParent(); // 31;
接下来改成这样;
var oParent = parentFun();oParent(); // 31;oParent(); // 32;oParent(); // 33;
我们发现每调用一次,c的值就就大了1,这是因为b并没有被回收,他被保存在了内存之中,这就符合了局部变量不能被GC回收这一概念,而且a与b都是函数childFun外部变量,两者条件都满足当然可以称为闭包;
看下面另一个例子;
function init(){ this.a = 10; this.b = 20;}init.prototype.sub = function(){ return this.c = this.a + this.b;}var oInit = new init();console.log(oInit.sub()); // 30;
这种写法是闭包吗?当然是,函数外部的局部变量被调用了;
function init(){ this.a = 10; this.b = 20;}function sub(){ init.call(this); return this.c = this.a + this.b;}var aSub = new sub();console.log(aSub.c);
利用call方法继承了函数init中的变量,满足了函数调用了外部的局部变量;所以自然是闭包;
最后看一个例子:
var obj = { firstName : 'li', rFun : function(lastName){ var that = this; return { lastname : lastName, setName : function(){ return that.name = that.firstName + this.lastname; } } } }var name = obj.rFun('shimin').setName()console.log(name) // lishimin;
对于了setName对应的函数来看,其中使用到了局部变量firstName和lastname;所以算的上是闭包;
上面说了那么多闭包,那闭包到底用来干什么的呢?
其实上面的例子很明显:
- 因为闭包内部调用存在变量不能被GC回收,所以可以用来作为缓存;
- 可以用来做封装(最后一个例子)
- 可以用来做继承(例三与例四)
-
可能不了解闭包,开发起来好像没有什么影响,业务上来说,好像都没有阻碍我们开发。
是这样的,但是我们开发中的确用到了大量的闭包,但是如果想要写高级的函数,如果不了解闭包,其实很难很好的去写成的,所以了解这一概念还是很有必要的。
好了,关于闭包就写到这里了,感谢大家的阅读,如有不正确的地方,烦请指正,感谢;
微信搜索关注公众号 【大前端js】,回复vue教程,react教程,webpack实战等等,获得不同的视频教程,大量视频教程等你来拿;
原创不易,总结不易,手打不易,转载时请注明出处,谢谢
- javascript 高级系列之闭包(closure)
- JavaScript之闭包closure
- Swift系列之闭包(Closure)
- JavaScript 之理解closure(闭包)
- Javascript闭包(Closure)
- Javascript--闭包(closure)
- Javascript闭包(Closure)
- Javascript闭包(Closure)
- javascript closure 闭包
- Javascript闭包(Closure)
- [Javascript] 闭包 closure
- Javascript闭包(Closure)
- JavaScript闭包(Closure)
- 再谈javascript闭包--Javascript高级用法 ----在Javascript中,什么是闭包(Closure)
- 闭包之Closure
- JavaScript中的闭包(Closure)
- C#、Javascript闭包(closure)
- Javascript闭包(Closure)
- 面向对象--单例设计模式(2)
- eigen使用
- 基于ormlite框架SQLite数据库的使用
- c#图片旋转,保存图片的时候一定要选择图片保存的格式
- 配置OpenGL开发环境(vs2015)
- javascript 高级系列之闭包(closure)
- 知识积累 jpa关键字
- 6-12 二叉搜索树的操作集(30 point(s))
- JavaWeb编码问题
- nodeJS ——nodeJS基础知识思维导读
- CG绘画基础教程之素描光影关系
- 斯坦福机器学习笔记_1
- C#:检测到有潜在危险的 Request.Form 值
- EDIUS HD非编系统,图形编辑工作站