前端学习笔记之闭包——看了一张图终于明白啥是闭包了

闭包

闭包定义:指拥有多个变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
函数内部可以直接读取全局变量。
函数内部变量无法在函数外部访问。
函数内部声明要用var或者let声明,不然会变成全局变量
链式作用域:子对象会一级级向上寻找父对象的变量,父对象的变量子对象都是可见的,反之则不行。
在一个闭包环境内修改变量值,不会影响另一个闭包中的变量。
普通的函数内嵌,内部函数是先执行;而闭包则是:先把内部函数赋给外部函数,然后在执行。

下面这段代码就是一根典型的闭包

function f1(){
    var a = 10;
    function f2(){
        alert(a);
    }
    f2();   //①
}
f1();       //10  ②

f1f1()的区别不加括号是代码,加()是执行这段代码,加return是返回一个值,可以把返回的值赋值给变量,不加return默认返回undefined

所以处有三种写法:

第一种:处写f2();处调用需要这样写f1();。具体执行过程:f1体内调用f2函数,并执行。

第二种:处写return f2();处调用需这样写f1();。具体执行过程:同上;区别是多了个return,因为现在f2函数中没有返回值,所以f1在调用f2只是执行一下alert(a)f1的返回值是undefined

第三种:处写return f2;处调用需这样写f1()();。这里返回的是f2函数的代码,所以在调用f1时要加上2个括号,第一个括号是执行f1函数,第2个括号是执行f2函数,如果处省略return会报错。

return和函数调用时是否加括号的意思都明白,但是把它俩结合起来,就搞不清了。

正好今天学闭包时碰上了,顺便就把它搞清楚了。

到底什么是闭包

对于新人(当然了是说我了),看很多闭包的定义,代码,还是不知啥是闭包,云里雾里的,这里感谢方方老师的文章JS 中的闭包是什么?,看完后,虽然还是说不出啥是闭包,但现在已经知道啥是闭包了,果然用图说话最牛逼。(图在文章中,我就不放出来了)

闭包的应用

MDN 上这个例子也写的很好

image

调用Counter.value()时,返回的是Counter内部的变量privateCounter
increment内部没有返回值,这个方法只是执行了privateCounter + 1操作,没有返回值;
同理decrement是将privateCounter - 1,也没有返回值;
所以执行Counter.increment,会返回undefined,但是接着操作Counter.value()时就可以得到1,因为执行上一步Counter.incrementprivateCounter+1了。

今天在下面三段代码上花费了大量的时间,一直似懂非懂,心里不踏实。

代码一:

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return this.name;
    }
};
obj.getName(); //object
(obj.getName = obj.getName)(); //window 非严格模式下

代码二:

var name = 'window';
var obj = {
    name: 'object',
    getName: function() {
        return function(){
            return this.name;    
        }
    }
};
obj.getName()(); //window

代码三:

var name = 'window';
  var obj = {
    name : 'object',
    getName : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
obj.getName()();    //object

今天在看阮一峰的博客学习Javascript闭包(Closure),对代码二、代码三部分很是不解,看到一网友搬出犀牛书(还没看过,我买了红宝石书才看了一点点)里的话,实在不解什么是作为函数调用,什么是作为方法调用;

《Javascript权威指南》上说:如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下); 如果嵌套函数作为方法调用,其this值指向调用它的对象。

又有一位网友说

每个函数在被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量(这一点通过前面的图可以看得更清楚)。意思就是说找到匿名函数中的this和arguments就不会再往下找了(这里的往下指的是外层的包含函数,和最外层的window全局环境),而匿名函数的this对象通常指向window,所以输出的是全局的那个字符串。不过,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

看到这里大概明白匿名函数的作用域是全局,继续翻看下面评论,大概意思是说“把this保存在obj作用域下的一个变量中,this就在当前函数的作用域下了”。直到看完也是,似懂非懂,反正就是感觉哪里不对劲,但也说不上了。

直到看到【JavaScript】【函数】闭包闭包!这篇文章的代码一部分,终于明白其中的逻辑了。

下面就来分析其中的逻辑,我分析的方法就是把不懂的地方一个个用console打印出来

代码二和代码一的区别是多了一层嵌套函数,this值就不一样了。
ps:我一开始以为在代码二中再嵌套一层函数,就会打印出object =_=|||

先来看代码一,明白之后,另外两段代码自然就懂了。

为什么obj.getName()打印出来的是object,因为这时getName方法是在obj的作用域下,所以this指向obj,返回值当然就是object了。

接着看(obj.getName = obj.getName)()删掉右边后,打印出的结果变成了object,这就纳闷了。
ps:第一眼看上去,这啥玩意,把自己赋值给自己?这不是多此一举,直接用不就行了!

console.log打印出obj.getName后,终于拨云见天,obj.getName = obj.getName这句话的意思就是把getName函数赋值给自己,这个时候就不是obj.getName,而是getName匿名函数了,匿名函数通常用的方法是()()立即执行,此时再看匿名函数已经脱离obj了,当然this也就指向了全局,打印出window

再来看代码二,用console打印出obj.getName()会发现是一个匿名函数,而匿名函数的this通常会指向全局,所以也就不难理解了

理解上面两段代码,代码三也就很好理解了。

闭包中引用循环变量

廖雪峰的闭包在文中就很形象的讲解了函数中的引用会变化的变量会有什么后果,我节选了他的结论和代码。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

f1(); // 1
f2(); // 4
f3(); // 9

这里的核心就是立即执行,如果不是立即执行的话,变量i就是for循环结束后的值了。

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

推荐阅读更多精彩内容

  • 很多年了,一直做着一个有关于你的梦。 梦里,时常是一个灯光闪耀的舞台,一段清浅的乐曲,一个翩翩而来的舞者,...
    五月慕晴阅读 636评论 0 1
  • 姓名:黄淑宜 公司:珠海三环知识产权 2017年10月1日打卡 第292A期乐观三组 日精进打卡第28天 【知~学...
    淑宜阅读 218评论 0 0
  • hello! everybody ! 在经历人人生中最重要的考试后,紧张的身心得以放松,所以…… 我与丹姐...
    傲娇祺阅读 212评论 0 0
  • 细雨织春与君别 听不得雨声纷沓檐前落 云碎柔肠珠泪脱 忍不住窗幔微启窥春色 乍现玉树翩粉蝶 喜斯雨亦新 惊斯花亦奇...
    水璇阅读 256评论 0 4