Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

闭包、垃圾回收机制回收闭包 #78

Open
TieMuZhen opened this issue Dec 28, 2021 · 0 comments
Open

闭包、垃圾回收机制回收闭包 #78

TieMuZhen opened this issue Dec 28, 2021 · 0 comments

Comments

@TieMuZhen
Copy link
Owner

TieMuZhen commented Dec 28, 2021

什么是闭包

闭包属于一种特殊的作用域,称为静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。

闭包优缺点

优点:

  • 可以使变量长期存储在内存中。
  • 可以访问函数内部成员。
  • 避免全局变量的污染。
  • 私有成员的存在。

缺点:

  • 当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过渡的闭包可能会导致内存泄漏,或加载过慢

闭包过程详解

function test1(){
    function test2(){
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2;
}
var c = 3;
var test3 = test1();
test3();

上面代码是一个闭包,下面将对原型链过程详细描述

一、当test1函数被定义时

function test1(){ }
var c = 3;
var test3 = test1();

当test1函数被定义时,系统生成[[scope]]属性,[[scope]]保存该函数的作用域链,该作用域链的第0位存储当前环境下的全局执行期上下文GO,GO里存储全局下的所有对象,其中包含函数test1和全局变量c

二、当test1函数被执行时(前一刻)

function test1(){
    function test2(){ }
    var a = 1;
    return test2;
}
var c = 3;
var test3 = test1();

当test1函数被执行时(前一刻),函数test2被定义

三、当test1函数被执行结束时

function test1(){
    function test2(){ }
    var a = 1;
    return test2;
}
var c = 3;
var test3 = test1();

当test1函数被执行结束时,因为test2被返回到外部,且被全局变量test3接受。这时test1的AO并没有销毁,只是把线剪断了,test2的作用域链还是连着的

四、test3执行时

function test1(){
    function test2(){
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2;
}
var c = 3;
var test3 = test1();
test3();

test3执行时,test2的作用域链增加自己的AO,当打印a的时候,在自己的AO上没有查找到,则向test1的AO查找。再次执行test3时,实际操作的仍然是原来test1的AO

五、test3执行结束后

function test1(){
    function test2(){
        var b = 2;
        console.log(a);
    }
    var a = 1;
    return test2;
}
var c = 3;
var test3 = test1();
test3();

当test3执行结束后,test2的AO被销毁,但原来test1的AO仍然存在且被test2连着

示例

示例1

function fun(n, o) {
    console.log(o);
    return {
        fun: function(m) {
            return fun(m, n);
        }
    }
}
var a = fun(0);
a.fun(1);

// undefined
// 0

示例2

function fun(n, o) {
    console.log(o);
    return {
        fun: function(m) {
            return fun(m, n);
        }
    }
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);

// undefined
// 0
// 0
// 0

示例3

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

垃圾回收机制

首先JavaScript具有自动垃圾回收机制,会找出不再使用的变量,然后释放其占用的内存。常用的方法有两种,即标记清除引用计数

引用计数

语言引擎有一张引用表,保存了内存里面所有的资源(各种值)的引用次数。如果一个值的引用次数为0,就表示这个值不再用到了,因此就可以将这块内存释放。但是引用计数有个最大的问题: 循环引用。

function func() {
    let obj1 = {};
    let obj2 = {};

    obj1.a = obj2; // obj1 引用 obj2
    obj2.a = obj1; // obj2 引用 obj1
}

当函数func执行结束后,返回值为undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1obj2的引用次数都不为0,所以他们不会被回收。

要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。上面的例子可以这么做:

obj1 = null;
obj2 = null;

标记清除

JavaScript 中有个全局对象,浏览器中是window。定期的,垃圾回收期将从这个全局对象开始,找所有从这个全局对象开始引用的对象,再找这些对象引用的对象...对这些活着的对象进行标记,这是标记阶段。清除阶段就是清除那些没有被标记的对象。

参考文献

@TieMuZhen TieMuZhen changed the title 闭包 闭包、垃圾回收机制回收闭包 Apr 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant