什么是闭包?

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
                                                  —— 你不知道的JavaScript(上卷)

一、闭包的定义

1、闭包的构成
首先,闭包由两部分构成:函数创建该函数的环境,环境由闭包创建时在作用域中的任何局部变量组成。

JS的变量作用域
变量的作用域有两种:全局变量和局部变量。
函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。

那么,如何从外部读取函数内部的局部变量?在下面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。
这就是Javascript语言特有的 链式作用域 结构(chain scope):子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,就可以在f1外部读取f1的内部变量。

function f1(){
  var n=999;
  function f2(){
    alert(n); 
  }
}
var test = f1();// 外部函数调用之后其变量对象n本应该被销毁
test();// alert 999,但闭包阻止了它们的销毁,所以仍然可以访问外部函数的变量对象

上面代码中的f2函数,就是闭包。
在JS中,只有函数内部的成员才能读取局部变量。所以在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

二、闭包的特性

在JavaScript中,外部函数调用之后其变量对象本应该被销毁,但闭包阻止了它们的销毁,所以仍然可以访问外部函数的变量对象。

function addCalculator (x) {
    return function (y) {
        return x + y;
    }
}

var add1 = addCalculator(1);
console.log(add1(1)); //2

add1 = null;// 释放对闭包的引用
console.log(add1(1)); //Uncaught TypeError: add1 is not a function

通常情况下,函数的作用域及其所有变量都会在函数执行结束后被销毁。
但如果创建了一个闭包的话,这个函数的作用域就会一直保存到闭包不存在为止。

闭包的特性一般为以下几点:
1、闭包一般为外部函数嵌套的内部函数、以及创建该内部函数的环境组成的;
2、闭包可以引用外部函数的参数和变量,即可以访问外部的环境;
3、闭包未被释放回收的情况下,若闭包中引用了外部函数的参数和变量,即使外部环境已经被释放回收,但是外部函数的参数和变量依然不会被垃圾回收机制回收。

三、闭包的作用

1、通过闭包来模拟私有方法
私有方法有利于限制对代码的访问,而且可以避免非核心的方法干扰代码的公共接口,减少全局污染。
下面的示例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为模块模式(module pattern)

var calculator = (function(){
    var a = 1;
    function addCalculator(val){
        a += val
    }
    return {
        add1:function() {
            addCalculator(1);
        },
        add2:function() {
            addCalculator(2);
        },
        result:function() {
            return a
        }
    }
})();

console.log(calculator.result());  // 1
calculator.add1();
console.log(calculator.result());  // 2
calculator.add2();
console.log(calculator.result());  // 4

在之前的示例中,每个闭包都有它自己的词法环境。而这次例子中只创建了一个词法环境,为三个函数所共享:calculator.add1,calculator.add2和 calculator.result。

该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 a的变量和名为 addCalculator的函数,这两项都无法在这个匿名函数外部直接访问,必须通过匿名函数返回的三个公共函数访问。

这三个公共函数是共享同一个环境的闭包,它们都可以访问 a变量和 addCalculator函数。

四、闭包的优缺点

1、优点

  • 保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突
  • 在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
  • 匿名立即执行函数可以减少内存消耗

2、缺点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以滥用闭包的话会造成网页的性能问题,在IE(IE9)之前可能导致内存泄露。
    解决方法:在退出函数之前,将不使用的局部变量全部删除(例如手动赋值为null)
  • 由于闭包涉及跨域访问,所以会导致性能损失。
    解决方法:可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。

引申:为何闭包的不当使用会在IE(IE9)之前可能导致内存泄漏,即无法回收变量的问题?
因为IE(IE9)之前的JavaScript引擎使用的垃圾回收算法是引用计数法,对于循环引用将会导致GC(Garbage Collection)无法回收垃圾。
注:在后面的章节中,我会找机会继续讨论什么是GC(Garbage Collection)

PS:更新来啦O(∩_∩)O,下一篇文章讲到了GC(Garbage Collection):JS的垃圾回收机制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351