JavaScript中闭包的本质

闭包是JavaScript开发人员常常谈论的问题,大家普遍对闭包的认知如下:

模糊的认知:闭包是定义在函数内部的函数;
清晰的认知:闭包是会保存它引用到的外部变量的特殊函数;

其实在JavaScript语言中,以上2种认知都是错误的;为了帮助大家正确地认识闭包,现分享出我对闭包的研究和理解,如下:(若想更深入地理解JavaScript的各种特性,可以参考另一篇文章:《JavaScript的发现与理解》)

1. 闭包

我对闭包的定义是:
闭包的标准定义:携带外部变量的函数称为闭包;

我之所以这样对闭包下定义,是因为这个定义几乎适用所有语言的闭包,如:Object-C、Swift、JavaScript等等;所以我认为这是较标准的定义;

对于JavaScript中的闭包虽然符合标准定义,但是由于JavaScript语言的一些特性,使得JavaScript中的闭包的实现与其它语言(如:Object-C、Swift)的实现并不一样;

很多人都认为JavaScript中的闭包只会携带它内部引用的外部变量,并不会携带没有引用的外部变量,其实这是错误的;可以通过下面的代码证明:

function outFun() {
    var outArg1 = "外部参数1";
    var outArg2 = "外部参数2";
    function outArg3() {
        console.log("外部参数3");
    }


    /*定义闭包
    * codeStr:字符串类型的参数,该参数的值将被当作代码执行
    * return : 返回将codeStr作为代码执行的结果;
    * */
    function closureFun(codeStr) {
        console.log("闭包引用的变量的值:",outArg1);
        return eval(codeStr);   //返回将codeStr作为代码执行的结果;
    }

    return closureFun;
}
 
var getValueOf  = outFun();     //获取闭包




var arg2Value = getValueOf("outArg2");      //尝试获取闭包内没有引用的变量outArg2的值;
console.log(arg2Value);     //输出结果为:外部参数2


var arg3Value = getValueOf("outArg3");      //尝试获取闭包内没有引用的函数outArg3;
arg3Value();     //输出结果为:外部参数3

从示例代码中的运行结果中可以看出,对于闭包引用到的外部变量outArg1 和 闭包没有引用到的变量outArg2和函数outArg3,在闭包执行时都能被正确地访问到,所以闭包会携带所有的外部变量(函数也是变量);

为什么会这样呢?若要理解,还需先了解一下作用域链的知识,下面是我对JavaScript的作用域链的理解:

2. 作用域链的理解

  1. 可以把作用域链理解成是一个栈结构;
  2. 每个作用域都有一个作用域对象用于保存在该作用域内创建的变量(包括函数),其保存的方式是:在作用域内创建的变量会成为作用域对象的属性;
  3. 作用链链保存的是各级作用域对象的引用,其中最近的作用域的作用域对象在最前端,越远的作用域的作用域对象越靠后;
  4. 全局作用域的作用域对象是全局对象本身;所以,每个作用域链的最后端都是全局对象的引用;
  5. 在全局作用域内创建的变量会成为全局对象的属性的原因:由于2(在作用域内创建的变量会成为作用域对象的属性)和4(全局作用域的作用域对象是全局对象本身),所以在全局作用域创建的变量会成为全局对象的属性;
  6. 函数的作用域链是在函数对象被创建时(被定义时)创建的;
  7. 每当函数被执行时,都会新创建一个函数的作用域对象,并把该作用域对象推到作用域链的最前端;
  8. 每当函数执行结束时,都会把函数的作用域对象从该函数作用链中推出;

3. 闭包的本质

其实闭包携带外部变量的机制并非闭包的特有机制,它是函数的作用域链的一个效应;在JavaScript中,闭包和普通函数没有任何本质的区别,闭包只是函数在某种使用场景下的一个名字,就好比凶器只是刀在用于行凶时的名字;

JavaScript中的闭包能携带外部变量的原因是:
JavaScript的函数在被创建时(被定义时)会生成自己的作用域链;该作用域链会保存各级作用域对象的引用,所以JavaScript的函数能够访问其外部的所有变量;
详见上文的< 作用域链的理解 >

所以,本质上,JavaScript中的闭包携带的不是外部变量,而是外部的作用域对象;

4. 使用闭包的建议

由于JavaScript中的函数(包括闭包)会创建并携带外部的作用域链;所以,建议:

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

推荐阅读更多精彩内容