道招

《浏览器工作原理与实践》笔记之闭包问题解答

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

《浏览器工作原理与实践》笔记之闭包问题解答

Q:

function fn() {
    var a = 10
    function f1() {
        console.log(a)
    };

    function f2() {
        console.log('f2')
    };
    f2();
};

fn();

我在函数f2里打断点,当执行到函数f2时,chrome里显示Closure:{a:10},如果把这个原因解释为在fn函数里会预扫描f1函数,那我现在把fn2函数和调用都注释了,现在执行fn函数时不产生Closure,为什么就不预扫描f1函数了?这是为什么?

A: 你把f2注释了,当执行fn函数时,照样会预扫描f1,照样会产生闭包,只不过当fn执行结束之后,闭包的内容没有外部引用,那么下次垃圾回收直接把比闭包的内容回收掉


Q:

  • 从内存模型角度分析执行代码的执行流程第二步看,在堆空间创建closure(foo)对象,它是存储在foo函数的执行上下文中的。 那么closure(foo)创建开始时是空对象,执行第三步的时候,才会逐渐把变量添加到其中。
  • 当foo函数执行结束后,foo的执行上下文是不是销毁了?如果销毁了,产生一下两个疑问:
    1. 如果foo函数执行上下文销毁了,closure(foo)并没有销毁,那foo函数执行上下文是怎么销毁的呢?就比如销毁一个盒子,盒子毁里,里面的东西应该也是毁掉的
    2. 既然closure(foo)既然没有销毁,那它存储在堆中的什么地方呢?毕竟它所依赖的foo执行上下文已经不存在了

A: 关于foo函数执行上下文销毁过程:foo函数执行结束之后,当前执行状态的指针下移到栈中的全局执行上下文的位置,foo函数的执行上下文的那块数据就挪出来,这也就是foo函数执行上下文的销毁过程,这个文中有提到,你可以参考“调用栈中切换执行上下文状态“图。

第二个问题:innerBar返回后,含有setName和getName对象,这两个对象里面包含了堆中的closure(foo)的引用。虽然foo执行上下文销毁了,foo函数中的对closure(foo)的引用也断开了,但是setName和getName里面又重新建立起来了对closure(foo)引用。

你可以:

  1. 打开“开发者工具”
  2. 在控制台执行上述代码
  3. 然后选择“Memory”标签,点击"take snapshot" 获取V8的堆内存快照。
  4. 然后“command+f"(mac) 或者 "ctrl+f"(win),搜索“setName”,然后你就会发现setName对象下面包含了 raw_outer_scope_info_or_feedback_metadata,对闭包的引用数据就在这里面。

Q:

  • Function 函数类型也是继承于Object,声明函数后是不是也是存在堆空间中的,那么浏览器编译函数时是不是会同时创建执行上下文和向堆空间中压入一个值
  • function a(){
    var b = 1;
    var c = {
        d: 2
    };
    }

    当 a 的执行上下文销毁后,c 对象在堆空间中的引用会跟着销毁么,将 c 返回出去或不返回,会不会是不一样的情况

A: 函数就是一种特别的对象,所以会保存在堆上,编译函数时,这个函数就已经存在于堆中了!

第二个问题返回了c对象的话,那么说明全局环境对c对象有引用,既然有引用那么就不会被垃圾回收器标记出来,所以c对象也就不会回收!

更新时间:
上一篇:《浏览器工作原理与实践》笔记之从堆栈空间看闭包过程下一篇:《浏览器工作原理与实践》笔记之垃圾回收

相关文章

关注道招网公众帐号
道招开发者二群