最后一次说说闭包

原创 老姚 随笔 我也来说说系列 13957阅读 2016-03-04 19:06:09 举报

人对事物的认识是不断深化的,同样,我对闭包的认识也是如此。
对于闭包,本人已经写过几篇。这里最后一次写篇关于闭包的文章。

虽说是写闭包,但要说说本文的关注点。
讲闭包,可以讲闭包产生的原因啦。什么词法作用域啦。
讲闭包,可以讲闭包如何应用啦。什么封装私有变量啥的。
讲闭包,可以讲闭包的好处,优缺点啦。什么占用内存啥的。
讲闭包,可以讲如何避免闭包。即本来不想使用闭包,不小心创建了闭包,以及由此产生的问题。
blah、blah。。。
总之等等吧。
然而,本文的关注点是平常我们写的代码,哪些是闭包,哪些又不是。仅此而已。

个人总结产生闭包的三个条件是:
1.调用的函数是父级作用域内部声明的
2.调用的函数是在父级作用域之外进行调用
3.调用的函数内部使用了父级作用域的内部变量

来看一个例子
[quote]var outer = function() {
var x;
var inner = function(){
return x;
};
return inner;
};
var fn = outer();
fn();[/quote]
调用的函数是谁?
是fn,因为函数都是引用类型,所以也是inner,
而inner是变量,其缓存是一个匿名函数(即function(){ return x; })。
这个匿名函数是在outer内部声明的。
这个匿名函数最后是在outer之外调用(不是内部)。
这个匿名函数内部使用了outer的内部变量。
这三个条件一旦满足就产生了闭包。

那么到底什么是闭包??
说法多了去了。随便百度。
我这里就管调用那个函数叫闭包。

产生的闭包的条件说清楚了。
不是闭包的例子
那么我们就来看看哪些东西不是闭包?
1.只满足第1个条件
[quote]var fn = function(x) { return x; };
fn(2);[/quote]
跟我说这是闭包,打死我也不同意。
有的人非要说“广义的讲,所有函数都是闭包”。
但是啥呢?好比,先有了“人”的概念,后来又根据一些特征得出了“小孩”的概念。然后你就可以说“广义的讲,所有人都是小孩”?。
从逻辑学上内涵和外延两个概念来说,这个比喻可能不太恰当。
个人觉得新概念肯定有不同之处,才提出的。我是不接受这种广义说法的。哪怕是权威的书。
当然,我们也没有必要去纠结这些说法。
上面之所以不是闭包,因为函数既没有引用外部变量(全局作用域变量),又没在父级作用域之外调用(全局作用域是最大的,所以没有之外了)。
普通函数都不是闭包的话,那匿名函数就更别提了。哪怕你长如下的样子:
[quote]var r = (function(x) {
return {
x: x
};
})(100);[/quote]
这跟先声明一个有名字的函数,再调用,没啥区别。

再看,
[quote]var fun1() {
var fun2 = function(x) { alert(x); }
fun2('xxx');
}
fun1();[/quote]
fun2也只满足第一个条件,因此也不是。

2.只满足条件1和3
其实第2个条件才是最重要的,必须在父级作用域之外调用才行。
因此,闭包必须得是局部函数才行。所以如下代码也不是
[quote]var name = 'xxx';
function say() {
return name;
}
say();[/quote]
假如函数是局部函数
[quote]var a = function() {
var x = 0;
function b() {
alert(x);
}
b();
}
a();[/quote]
因为b的调用,没有在a的作用域之外调用。因此b也不是闭包。

其他情况,其他组合条件,不举例子了。

闭包举例
这里主要来看看怎么个外部调用法儿。

1.作为函数返回结构的一部分
[quote]var fun = function() {
var x = 0;
return {
fn: function() {
alert(++x);
}
};
}
var r = fun();
r.fn();
r.fn();[/quote]
r.fn是fun的内部函数(注意函数是引用类型的),调用是在fun的外部调用的。

2.赋给外部变量
[quote]var fun1;
var fun2 = function() {
var x = 0;
fun1 = function() {
alert(x);
}
}
fun2();
fun1();[/quote]
其中fun1是个闭包,条件都满足。有疑问的地方可能是,是否内部声明的呢?是的,因为fun1是个函数,是fun2的内部函数。

3.异步操作,绑定到dom事件
[quote]var fun2 = function() {
var btn = document.querySelector("#myBtn");
var x;
btn.onclick = function() {
alert(x);
}
}
fun2();[/quote]
主要看看是否是外部调用。是的。因为用户点击时触发事件,不是在fun2中内部调用的。
如果btn是外部声明的,跟情况2差不多。

4.异步操作,setTimeout或setInterval
[quote]var fun = function(x) {
setInterval(function() {
console.log(x++);
}, 3000)
}
fun(1);[/quote]
setInterval第一个参数,是一个函数,此函数的执行必须是在全局调用的。因此是闭包。

5.异步操作,ajax请求回调
不举例子了。。原谅我的任性

6.特权函数
[quote]var Person = function() {
this.sayName = function() {
alert(name);
}
var name = "laoyao";
}
var p = new Person();
p.sayName();[/quote]
上面我故意把sayName和name写反的,没有问题的。
只要是闭包,使用的外部变量位置“在哪”都可以的。当然闭包不是本质原因,只要在函数调用之前就行了。而闭包是在外部调用的。
这里来说说它为啥是闭包呢?
p.sayName是个构造函数内部声明的匿名函数,没问题。
调用时也是在构造函数之外调用。

写到这里,回想当初写的第一篇文章说闭包是一个函数的返回值,并引用了函数内部变量。现在看来还是太年轻。让怨念先飞一会儿。
最后,再来一个更深层的例子。
[quote]var fun1 = function() {
var x = 1;
return function fun2() {
var y = 5;
return function fun3() {
alert("x=" + (++x) +";y=" +(++y));
}
}
}
var fun2 = fun1();
var fun3 = fun2();
fun3();
fun3();[/quote]
fun2和fun3都是闭包。如果结合上面的几种情况,类似的例子能写出很多。

本文完。
等下!我在想,一年半载后,会不会又觉得,这仍不是我最后一次写闭包呢?
以后还是多写写源码分析的文章。

评论 ( 29 )
最新评论
老姚 28F 2016-11-12 17:57:47 29F

内存管理,个人涉及不深,你说的理解压栈就能理解闭包,有没有相关文章地址留一下我去瞧瞧。至于我说岂不是本质,是因为涉及内存管理的东西,都属于是如何实现的,“本质”更关心是为啥要有闭包,自然而然的形成原因。也是个人见解。就像promise的本质是指如何实现控制反转,并且回调只执行一次,解决回调嵌套只是副产品。如何实现就不能算本质了。也比如原型的本质,是提供一种委托机制,而不是继承。一种不同于类新的面向对象编程思路。至于内部是咋实现的,知道大概就好。而压栈对于闭包来说,是怎样的层面,百度了一下输入相关关键字,我没找到。能给个地址那就好了,先谢了。

前端李李 27F 2016-11-12 14:17:45 28F

你也了解过内存管理吗?

老姚 26F 2016-11-12 11:23:01 27F

压栈不是本质原因。js词法作用域和函数的一等公民是本质原因。此观点不是我说的,详细请看《程序设计语言概念》

前端李李 2016-11-12 11:02:00 26F

哈哈 .......函数就是闭包。 理解压栈就能理解闭包。

KrystalWang 2016-07-26 15:03:55 25F

明白了 90% 1.内部声明 2.外部调用 3.访问内部变量

qq393008294 2016-03-10 14:10:42 24F

新手一脸茫然的样子

老姚 16F 2016-03-09 15:34:22 23F

闭包的作用,很多书上都有的,话说上面的那些例子。就是我们随便写出的代码,这也可以算是应用了。后续还会写一篇。

老姚 20F 2016-03-09 15:32:16 22F

看文章开头。

老姚 19F 2016-03-09 15:31:44 21F

可能会再写一篇。要不你看看文中补充的E问连接地址。

墨白 2016-03-09 14:57:28 20F

多看几遍好像有那么点了解了,大概就是一个函数里面声明的函数,里面的函数的触发条件是外部函数运行的情况下才能运行。。最后个例子很闹心

墨白 2016-03-09 12:09:29 19F

还没看完 ,头开始大起来了。。能不能说下闭包的应用好处或者优势什么的

Mengxuan01 2016-03-07 18:55:59 18F

我前几天又开始学闭包,哎、、、、

老姚 16F 2016-03-07 16:34:21 17F

不自觉你就会写出来。不信你看看你的代码。至于具体情况,请百度。可以提示你下,从正反两方面来看第三条。

小莫沫 2016-03-07 16:25:48 16F

首先谢谢博主的细心分享,现在对于什么是闭包有了一定的了解了,但是在什么情况下会使用闭包呢?

老姚 14F 2016-03-07 10:05:52 15F

是啊,绕了好几年

天明爱少羽 2016-03-07 09:50:02 14F

会这么用,但是概念什么的,想想都疼

greenhand 2016-03-06 18:59:46 13F

666

老姚 11F 2016-03-06 10:30:25 12F

是啊,多亏了大伙点赞和留言来的。写文章确实费时间的,不过也有好处,也是对知识梳理的过程。一旦理解错了,还能得到最快的反馈。

hugeannex 10F 2016-03-06 10:18:35 11F

发现你最近作品大爆炸啊,经常上头条了!而且教程写得够细,我就很少有这么耐心了,写两下教程,就不想写了。。。

老姚 7F 2016-03-06 10:15:18 10F

嗯,是这样的。概念的东西,只有在传递信息时,才会体现价值。