闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~

又或者是你代码中出现的闭包,并不是你刻意而为之的行为~ 又或者是因为能达到效果,也知道是闭包,但是原理却不知道?。。。。

一千个人就有一千个哈姆雷特~  每个人也许都有自己对闭包的理解, 我也不例外, 曾经N次百度过闭包,却没有真正的消化过这个知识点, 也曾工作中无数次运用过闭包, 却不知其所以然~~

所以,我想把我理解的闭包,自己总结一下,虽然很多都是自己的理解, 但是总结再更正,才会越来越好~~

首先是闭包的定义:

  • 维基百科对闭包的定义:
闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
  • 书籍《你不知道的Javascript》对闭包的定义: (个人觉得对闭包阐释最好的一本书)
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数实在当前词法作用域之外执行。

上面的概念基本上已经说明了闭包是什么,这只是一个概括,不知道的人看了之后还是不会知道,所以要知道闭包还是需要庖丁解牛一样的一层一层的去理解~~

给自己几个疑问? 然后对这些疑问一层一层的理解,直到理解为止!

  • 闭包产生的环境?
  • 闭包的作用?
  • 闭包的一些使用场景?
  • 闭包的弊端?

闭包的环境:

既然是叫做闭包,里面有个包字,那么肯定就是被包围的意思咯~ 竟然是被包围,肯定是在一个空间中,而JS能代表空间的是什么? 就是作用域,作用域又有全局作用域跟局部作用域。。

所以闭包产生的环境应该就是在作用域当中了, 所以有闭包肯定有局部作用域, 有局部作用域并不一定有闭包~~

闭包的作用:

竟然有闭包的存在肯定有其存在的价值, 那么闭包的作用体现再哪里呢~~

var a = 1;
function fn1() {
console.log(a); // var b = 2;
} console.log(b); //b is not defined
//当全局中有个变量a,我们在函数fn1中可以随时访问/修改这个变量,但是当我的函数fn1中有一个变量2的时候,我想在外面访问到这个b,很明显,失败了, 因为外部作用域是无法访问内部作用域的;

而闭包要做的事情就是让我们可以访问到变量b!

function fn1() {
var b = 2;
return function() {
return b;
}
} var b = fn1()();
console.log(b); //

显然,我们拿到了b的值,这达到了我们想要的效果, 但是,为什么这样可以拿到b的值呢?

  • 在返回的函数中,我们是可以拿到b的值得,因为内部函数是可以访问外部作用域中的任何变量;
  • 在返回的函数中,我们把这个值也给返回了,所有fn1中就会一直存在一个b=2的值;
  • 一般fn1函数调用后里面的b会被销毁,但是因为我们再它的返回函数中调用了这个b,所以这个b会一直存在于内存中,不会被销毁(这也是闭包的弊端之一,后面详解);
  • 这样,我们利用闭包的原理,把b的值给悄悄偷出来了~

从上面这个小例子中我们就可以知道,闭包的作用就是可以让外面不能拿到变量值得地方可以顺利的拿到这些值,虽然外部函数不能拿到内部函数的值是JS对其作用域的一种保护,虽然闭包破坏了这种保护,但却实现了一些通常情况下不能实现的功能。

闭包的一些使用场景:

闭包的使用场景真的是无处不在,虽然有可能你本身并没有察觉到,但是并不能否定它的存在~

在说闭包的一些实际应用的地方之前,先看一段JS中的闭包经典小案例~~

for(var i=1; i<=5; i++) {
setTimeout(function timer() {
console.log(i); //结果并不是我们想要的1-5, 而是连续出现5次6
}, i*1000);
}

为什么会是这样呢? 因为作用域问题, 之前说过有闭包就有作用域~ 这里只有个全局作用域, 在全局中只有一个i,所以每次改变i都只是对其赋予一个新的值;

在setTImeout方法执行时, 循环已经结束了,所以每次循环都是i++直到循环完成,变成了6;

因为JS中没有块级变量, 为了让每次i的值我们都可以拿到,所以我们要创造一个作用域用来存储变量i的值, IIFE(函数自执行)可以创造一个块级变量:

for(var i=1; i<=5; i++) {
(function(j) { //2,将i的值赋值给J
setTimeout(function timer() {
console.log(j); //3,j输出的就是每次循环中i的值
}, i*1000);
})(i) //1,每次循环拿到i的值
}
//执行结果 1,2,3,4,5

这样就实现的我们想要的结果,前面说,JS没有块级变量,如果有的话,是不是会更简单,ES5没有,但是ES6中有, 那么用块级变量怎么实现:

for(let i=1; i<=5; i++) {  //就是let
setTimeout(function timer() {
console.log(i);
}, i*1000);
}
//1,2,3,4,5

在实际的工作用,我们可能经常解除到的JS模块化,模块化的实现就是利用的JS中的闭包~~~

function moduleFn() {
var name = 'just'; var arr = [1,2,3]; function nameFn() {
console.log(name);
} function arrFn() {
console.log(arr.join("!"));
} return { //产生闭包! 在当前函数中返回了一个对象,这个对象中有nameFn,和arrFn, 这两个函数中又有了moduleFn中的变量。
nameX: nameFn,
arrX: arrFn
}
} var foo = moduleFn(); foo.nameX(); //just
foo.arrX(); //1!2!3 //利用闭包,我们就可以获取作用域中的值了!~~

正是因为闭包的这种特性,在很多场景中可以利用闭包大展身手~  但是,高手使用大招是需要废除内力的~~而闭包的内力~~~

闭包的弊端:

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值

能实现功能固然重要,但是保证性能也同样重要,这其中的取与舍,只有在不断的项目实战中去自由拿捏~

如果能对闭包的使用驾轻就熟,那么JS中的葵花宝典你也就修炼的差不多了~~

下面是一个在网上的闭包终极题目~  如果你都能够理解,那么恭喜你, 练成了葵花宝典了!~

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,?,?,?

答案自宫之后便会知道!~

JavaScript葵花宝典之闭包的更多相关文章

  1. 深入理解javascript原型和闭包 (转)

    该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和闭包,当然,肯定少不了原型链和作用域链.帮 ...

  2. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  3. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  4. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  5. 深入理解javascript原型和闭包(2)——函数和对象的关系

    上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...

  6. 深入理解javascript原型和闭包(3)——prototype原型

    既typeof之后的另一位老朋友! prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名.如果它还是您的新朋友,我估计您也是javascript的新朋友. 在咱们的第一节(深入理解 ...

  7. 深入理解javascript原型和闭包(4)——隐式原型

    注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看<javascript高级程序设计(第三版)>第6章:面向对象的程序设计. 上节已经提到 ...

  8. 深入理解javascript原型和闭包(5)——instanceof

    又介绍一个老朋友——instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

  9. 深入理解javascript原型和闭包(6)——继承

    为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...

随机推荐

  1. PhpStorm 相关激活方式

    点击进入下面网站: http://idea.lanyus.com/

  2. shell 1>&2 2>&1 &>filename重定向的含义和区别

    当初在shell中, 看到">&1"和">&2"始终不明白什么意思.经过在网上的搜索得以解惑.其实这是两种输出. 在 shell 程 ...

  3. WPF 自定义标题栏 自定义菜单栏

    自定义标题栏 自定义列表,可以直接修改WPF中的ListBox模板,也用这样类似的效果.但是ListBox是不能设置默认选中状态的. 而我们需要一些复杂的UI效果,还是直接自定义控件来的快 GitHu ...

  4. 域普通用户执行金蝶K/3权限不够解决方法

    一.问题 公司财务部的机器加入域后,用户一直授予本地管理员的权限,主管坚持要撤销管理员权限,而金蝶K3没管理员权限又无法执行. 报错信息为“注册表许可权不够,请参考安装目录的帮助档案进行许可权的配置. ...

  5. Redux状态管理方法与实例

    状态管理是目前构建单页应用中不可或缺的一环,也是值得花时间学习的知识点.React官方推荐我们使用Redux来管理我们的React应用,同时也提供了Redux的文档来供我们学习,中文版地址为http: ...

  6. AC日记——砍树 codevs 1388

    1388 砍树  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description 伐木工人米尔科需要砍倒M米长的木 ...

  7. 深度优先搜索(DFS)

    [算法入门] 郭志伟@SYSU:raphealguo(at)qq.com 2012/05/12 1.前言 深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍历的算法.它的思想是从一 ...

  8. CORS详解

    介绍 由于同源策略的缘故,以往我们跨域请求,会使用诸如JSON-P(不安全)或者代理(设置代理和维护繁琐)的方式.而跨源资源共享(Cross-Origin Resource Sharing)是一个W3 ...

  9. [LeetCode] Word Ladder II 词语阶梯之二

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...

  10. [LeetCode] Combinations 组合项

    Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. For exampl ...