杂记(2019/12/9-15)

2019/12/9 Monday

今天的零碎知识点

收尾上周遗留问题“call、apply、bind区别”

什么是闭包?闭包怎么形成的?

闭包是可以访问另一个函数内部变量的函数,闭包的使用则是函数在它定义时的作用域以外被调用。

闭包形成的本质其实是,存在指向父级作用域的引用

let value = 1; 
function f1() {
    let value = 2;
    function f2() {
       let value = 3;
       console.log(value);    //  这里引用了父级f1的作用域
    }
    return f2;
}
let x = f1();
x();    // 2

在ES5中存在两种作用域——全局作用域和函数作用域。

在上面这个栗子中,x是对f2函数的引用,而f2函数指向window、f1和f2本身的作用域,这就是一条作用域链。当访问value这个变量时,至底层往上寻找,先在f2本身的作用域中寻找,没找到就顺着作用域链到f1的作用域中寻找,找到了就返回该变量的值,如果找到window作用域了都没找到则报错。

下面的形式也行成了一个闭包,虽然它并没有返回一个函数:

let f2;
let value = 1;
function f1() {
    let value = 2;
    f2 = function() {
        console.log(value);
    }
}
f1();
f2();   // 2

f1函数执行后,f3函数就存在了指向父级作用域的引用,这个时候就形成了一个闭包,访问value变量时,在作用域链上寻找,最后返回结果。

2019/12/10 Tuseday

我发现我还得整理复习this,箭头函数!!!

之前学的实在太浅了,这个杂记专栏说是复习知识点,倒不如说是重新学习,总有不一样的收获!越学越觉得自己太太太菜啦!

加油!冲冲冲!

(看来心情好的时候不能得瑟,因为坏心情会马上找上门)

“我看到你相当积极地吮吸你的伤口,难怪它不能结疤。”
——三毛《亲爱的三毛》

我觉得博客是另一个我,是现在坐在电脑屏前的我的心情写照。虽然人类的悲欢并不相通,但是庆幸我能享受当下的孤独,学会和自己对话。

“我听到旷野,听到苍穹,听到荒漠和无边无际的黄土丘壑,听到炽热的沙漠,听到蓬勃生长的一切,有温度的、干燥到要燃烧的生命。”

你看,还有那么多美好,何必把自己框在现在的故事中。

简单的生活不必有太多完美,这世界也不该有太多伤悲!

我还有梦,还期待做梦,还敢做梦!

今天的零碎知识点

闭包的表现形式?(那些地方体现了闭包?)

  1. 返回一个函数:昨天的第一个栗子就是了

  2. 做为函数参数传递

    let a = 1;
    function foo(){
      let a = 2;
      function baz(){            
        console.log(a);     
      }
      bar(baz);
    }
    function bar(fn){
      fn();
    }
    foo();    

    错误思路:上面栗子的作用域链:baz作用域 => bar作用域 => foo作用域 => window。然后顺着作用域先找到a=3,输出。

  3. 在定时器,事件监听,Ajax请求或者任何异步操作中,只要使用了回调函数,实际就是使用了闭包!

    // 定时器
    setTimeout(function timeHandler() {
        console.log('延时了100毫秒')
    },100)
    
    // 事件监听
    $('#app').click(function() {
        console.log('DOM Listener');
    })
  4. IIFE(立即执行函数)创建闭包。

    var a = 2;
    (function IIFE(){
      // 输出2
      console.log(a);
    })();

2019/12/11 Wednesday

花了一个早上时间做凹凸透镜激光扩束系统的方案设计,窒息!最后还要基于MATLAB!!!

被退回来三次,第四次报告终于过了!!我原地360°螺旋上天给老师劈个叉,爱你啊啊啊!

(复习了一天模电之后…)

脑子仿佛不是自己的了,但是硬件实验室的同学真的好好呀,很耐心地给我讲解了知识点。

今天发现自己对闭包的深入学习远远不够,但是学友说得对,学习是循序渐进的,每一遍学习都不断加深。

2019/12/12 Thursday

今天的心情暖暖的!

从生活中又学到了一个小细节:

一个国外的家庭,在他们家门口给快递员和外卖员配备了小零食、饮料以及暖心的感谢标语,在监控中看到快递员收到这份爱之后在门口跳起了舞。

我觉得这个家庭一定是充满爱的,不止这个快递员,连看视频的我都感觉到了小温暖!

生活馈赠的温暖让心情变得暖暖的,嘴角的笑怎么都藏不住!

“宇宙山河浪漫,生活点滴温暖,都值得你前进。”

今天的零碎知识点

重新深入学习闭包作用域链

那天在总结闭包这道例题的时候,突然想着如果我给bar函数加一个变量a的赋值,那么输出结果会变成3吗?

我的想法是:作用域链:baz作用域 -> bar作用域 -> foo作用域 -> window。访问变量a时顺着作用域先找到a=3,输出3。

结果不出我所料!真的错误了呢!(毫无防备地留下了真正属于菜鸡的泪水…)

let a = 1;
function foo(){
  let a = 2;
  function baz(){            
    console.log(a);     
  }
  bar(baz);
}
function bar(fn){
  let a = 3;    // 加上这个
  fn();
}
foo();     // 输出结果还是2,不是3

深(da)入(lao)学(jiang)习(jie)之后,终于知道是怎么回事了。

原来我,作用域链没!!学!!明!!白!!

首先,重新讲明白作用域链,先看看下面的栗子:

function foo() {
    let a = 1;
    function bar() {
        let a = 2;
        function baz() {
            let a = 3;
            console.log(a);
        }
        baz();
    }
    bar();
}
foo();

在全局作用域中调用foo()时,创建了foo的临时OA对象:

foo-OA {
    a: 1
    bar: function()
}

在foo函数中调用了bar函数,同样生成了bar的临时OA对象:

bar-OA {
    a: 2
    baz: function()
}

在bar函数中调用baz函数,生成baz的临时OA对象:

baz-OA {
    a: 3
}

这个时候就可以将作用域链连起来啦:window -> foo-OA -> bar-OA -> bar-OA;

于是我们访问变量a的时候,顺着底层开始寻找,在baz-OA对象中找到了a=3的结果;如果我们把baz-OA对象中的a:3去掉,那么JS引擎会找到bar-OA对象中的a=2,同理往下类推,如果最后在全局中也没有找到,就会报错。

那么现在回头看看那个栗子:

作用域链:

window {
    a: 1
    foo: function()
}
-> foo-OA {
    a: 2
    baz: function()
    }
    -> bar-OA {
        a: 2
        baz: function()
    }
    -> baz-OA {
        a: -> foo-OA.a    // 访问了foo-OA的a,形成了闭包,把foo的a包裹到了它的作用域里。
    }

简单一句话就是:baz函数形成了闭包,把foo的a变量包裹到自己的作用域里,而baz作为参数传给了bar。

看看上面的作用域链,虽然baz没有自己作用域中的a变量,但是在定义的时候访问了foo-OA的a,形成了闭包,把foo的a包裹到了它的作用域里。所以在调用形成作用域链的时候,baz执行console.log的时候,要去找a变量,那么baz-OA中有a,所以输出2,并不用找到链的上层bar-OA中的a。

破案了破案了!喜大普奔哈哈哈哈哈哈!

2019/12/13 Friday

今天的继续整理

如何销毁闭包

看看这个简单的栗子,这是最简单的返回函数的闭包:

function foo() {
    let a = 4;
    return function() {
        console.log(a);
    }
}
let bar = foo();

当执行let bar = foo()时,形成了foo-OA对象:

foo-OA {
    a: 4
}

一般的函数执行完之后,临时OA是会被消除的,但是这里不会

bar -> 引用foo返回函数 -> 返回函数闭包中包裹着foo-OA中的a变量

因此bar一直保持着对foo-OA的引用,所以当foo()执行结束后并不会销毁foo-OA对象。

那么我们怎么使用闭包呢?

bar();

这样就成功调用了,并且形成了bar的临时OA:

bar-OA {
    a: -> foo-OA.a
}

在bar调用结束后,bar-OA被销毁了,但是bar对foo-OA.a的引用还存在呀!

那么我们只需要bar = null 就可以销毁引用,进而销毁foo-OA对象了。

2019/12/14-15 Two days

被电话考核了,然后发现我还得再复习复习如下内容:

  • 内存泄漏
  • 执行上下文
  • 匿名函数
  • sort并不会立刻排序,而是转换二进制
  • bigInt
  • 立即执行函数
  • this四种绑定

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 610193653@qq.com,谢谢啦!

文章标题:杂记(2019/12/9-15)

本文作者:zzzwyyy

发布时间:2019-12-09, 11:23:54

最后更新:2019-12-17, 21:34:23

原始链接:http://yoursite.com/2019/12/09/杂记(2019-12-9-15)/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录