人对事物的认识是不断深化的, 同样, 我对闭包的认识也是如此.
对于闭包, 本人已经写过几篇. 这里最后一次写篇关于闭包的文章.
虽说是写闭包, 但要说说本文的关注点.
讲闭包, 可以讲闭包产生的原因啦. 什么词法作用域啦.
讲闭包, 可以讲闭包如何应用啦. 什么封装私有变量啥的.
讲闭包, 可以讲闭包的好处, 优缺点啦. 什么占用内存啥的.
讲闭包, 可以讲如何避免闭包. 即本来不想使用闭包, 不小心创建了闭包, 以及由此产生的问题.
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 第一个参数, 是一个函数, 此函数的执行必须是在全局调用的. 因此是闭包.
- [quote]var Person = function() {
- this.sayName = function() {
- alert(name);
- }
- var name = "laoyao";
- }
- var p = new Person();
- p.sayName();[/quote]
- [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]
来源: http://www.qdfuns.com/article/17398/9b28ba7e036240b1252f1c82b9883d94.html