闭包+同步任务+异步任务的有趣小question

question 1:
给出需求:
需求: 点击某个按钮, 提示"点击的是第n个按钮"

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>00_引入</title>
    </head>

    <body>

        <button>测试1</button>
        <button>测试2</button>
        <button>测试3</button>
        <script type="text/javascript">
            var btns = document.getElementsByTagName('button')
            //遍历加监听
            for (var i = 0,length=btns.length; i < length; i++) {
                  var btn = btns[i];
                  btn.onclick = function () {
                    console.log('第'+(i+1)+'个')
                  }
            }
        </script>
</body>
</html>

请问这种写法为什么错误?以及当button被点击后,会打印出什么?

无论点击哪个一个按钮 都是打印出:"第4个"
因为同步任务的优先级>异步任务优先级,for循环执行完成后,才会执行dom事件的回调函数,这时候i=3

同一个需求,加一点改变,这样呢?

for (var i = 0,length=btns.length; i < length; i++) {
              var btn = btns[i]
              //将btn所对应的下标保存在btn上
              btn.index = i;
              btn.onclick = function () {
                alert('第'+(this.index+1)+'个')
              }
}

结果:

点第一个btn会alert 1,点第二个btn会alert 2,点第三个btn会alert 3
在执行同步任务的时候,给每个btn加了一个下标,可以写btn.index也可以写btn.a,看个人习惯,命名而已

question2:
这是一道比较经典的闭包面试题

<script type="text/javascript">
            //代码片段一
            var name = "The Window";
            var object = {
                name: "My Object",
                getNameFunc: function() {
                    return function() {
                        return this.name;
                    };
                }
            };
            console.log(object.getNameFunc()()); 
            //输出 the window

            //代码片段二
            var name2 = "The Window";
            var object2 = {
                name2: "My Object",
                getNameFunc: function() {
                    var that = this;
                    return function() {
                        return that.name2;
                    };
                }
            };
            console.log(object2.getNameFunc()()); 
            //输出 myobject
            
        </script>

question3:
也是闭包相关面试题,稍微绕了一点点:

function fun(n, o) {
                console.log(o);
                return {
                    fun: function(m) {
                        return fun(m, n)
                    }
                }
            }
            var a = fun(0);
            //输出undefined
            a.fun(1);
            //0;
            a.fun(2);
            //0
            a.fun(3);
            //0
            var b = fun(0).fun(1).fun(2).fun(3);
            //输出 undefined 0 1 2 
            
            var c = fun(0).fun(1)  //输出 undefined 0
            c.fun(2) //1 
            c.fun(3) //1

如何产生闭包?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

闭包到底是什么?

  • 使用chrome调试查看
  • 理解一: 闭包是嵌套的内部函数(绝大部分人)
  • 理解二: 包含被引用变量(函数)的对象(极少数人)
  • 注意: 闭包存在于嵌套的内部函数中

产生闭包的条件?

  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
// 1. 将函数作为另一个函数的返回值
            function fn1() {
                var a = 2

                function fn2() {
                    a++
                    console.log(a)
                }
                return fn2
            }
            var f = fn1()
            f() // 3
            f() // 4

闭包内部引用的变量,不会被作为垃圾回收


闭包的作用?

  • 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
    问题:
  1. 函数执行完后, 函数内部声明的局部变量是否还存在? 一般是不存在, 存在于闭中的变量才可能存在
  2. 在函数外部能直接访问函数内部的局部变量吗? 不能, 但我们可以通过闭包让外部操作它
<script type="text/javascript">
            function fn1() {
                var a = 2
                function fn2() {
                    a++
                    console.log(a)
                    // return a
                }
                function fn3() {
                    a--
                    console.log(a)
                }
                return fn3
            }
            var f = fn1()
            f() // 1
            f() // 0
</script>

闭包的生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
<script type="text/javascript">
  function fn1() {
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2
    function fn2 () {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>

闭包的应用—自定义js模块

闭包的应用2 : 定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包信n个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

有2种方法,先介绍第一种方法:

<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
  var module = myModule()
  module.doSomething()
  module.doOtherthing()
</script>

myModule.js的内容:

unction myModule() {
  //私有数据
  var msg = 'My atguigu'
  //操作数据的函数
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露对象(给外部使用的方法)
  //如果只暴露一个可以暴露函数,如果暴露多个,可以以对象形式
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}

第二种方法,使用了立即执行函数,建议使用第二种方法,就没必要执行一次myModule()

<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
  myModule2.doSomething()
  myModule2.doOtherthing()
</script>

myModule2.js:

(function () {
  //私有数据
  var msg = 'My atguigu'
  //操作数据的函数
  function doSomething() {
    console.log('doSomething() '+msg.toUpperCase())
  }
  function doOtherthing () {
    console.log('doOtherthing() '+msg.toLowerCase())
  }

  //向外暴露对象(给外部使用的方法)
  window.myModule2 = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})()

闭包的缺点以及解决

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

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,344评论 0 17
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,373评论 25 707
  • 一直以来岛国民族的怪异想象力着实让我们大吃一惊,这种怪异想象力体现在日本生活的方方面面,虽然有些奇葩,甚至觉得有些...
    浪尖的石头阅读 428评论 0 0
  • HTML(超文本标记语言) head title 标题标签 meta 标签 style(内部css样式) scri...
    那你很棒啊o阅读 188评论 0 0
  • 昨日因大勇要求,去临建区的老谭家饭店吃饭,对于近期过敏的我,不喝酒显得没氛围,但还是坚持了不喝酒,从时间的角度来...
    律人阅读 691评论 0 0