闭包,写过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. shell 脚本之循环使用 for while 详解

    任何一种编程语言中循环是比不可少的,当然 shell 脚本也少不了循环语句,包括 for 语句. while 语句.文中主要以实际用例来说明 for while 都有哪些常见的使用方法和技巧. 一.f ...

  2. Linux LVM学习总结——扩展卷组VG

    Linux服务器由于应用变更或需求的缘故,有可能出现分区空间不足的情况,此时往往需要进行扩容(要增加分区的空间),而采用LVM的好处就是可以在不需停机的情况下可以方便地调整各个分区大小.如下所示,分区 ...

  3. everything + autohotkey的配合使用

    一,everything是文件搜索神奇,瞬间定位到文件,在众多的文件中找到你需要的文件.(百度下载就好,分32位和64位)   二,autohotkey是热键启动设置,方便的打开常用的应用. 直接使用 ...

  4. 威佐夫博弈(Wythoff Game)

    有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者. ...

  5. Java开发的几个注意点

    原文出处: 后端技术杂谈 1. 将一些需要变动的配置写在属性文件中 比如,没有把一些需要并发执行时使用的线程数设置成可在属性文件中配置.那么你的程序无论在DEV环境中,还是TEST环境中,都可以顺畅无 ...

  6. Spring 下默认事务机制中@Transactional 无效的原因

    Spring中 @Transactional 注解的限制1. 同一个类中, 一个nan-transactional的方法去调用transactional的方法, 事务会失效 If you use (d ...

  7. [LeetCode] Maximum Depth of Binary Tree 二叉树的最大深度

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  8. [LeetCode] Decode Ways 解码方法

    A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...

  9. CentOS安装Redis详细教程

    构建 Redis redis 目前没有官方 RPM 安装包,我们需要从源代码编译,而为了要编译就需要安装 Make 和 GCC. 如果没有安装过 GCC 和 Make,那么就使用 yum 安装. yu ...

  10. centos 6.5 redis 安装

    安装教程有用的地址 https://my.oschina.net/u/2478188/blog/726984: 错误1 Redis: You need tcl 8.5 or newer in orde ...