js 闭包

博客分类: 江河计划

js 闭包

算法

多重背包问题 - 每个物品最多个 n 个

    const knapsack = (weights:number[], values:number[], numbers:number[], total:number) => {
        var n = weights.length;
        var f = [[]];
        f[-1] = new Array(total+1).fill(0);
        for(var i=0; i<n; i++){
            f[i] = new Array(total).fill(0);
            for(var j=0; j<=total; j++){
                if(j < weights[i]){
                    f[i][j] = f[i-1][j];
                }else{
                    for(var k = 1; k*weights[i] <= j && k<= numbers[i]; k++){
                        f[i][j] = Math.max(f[i-1][j], f[i-1][j-k * weights[i]] + values[i] * k);
                    }
                }
            }
        }
        var totalWeight = 0,
            j = total;
        for(var i=n;i--;){
            if(f[i][j] > f[i-1][j]){
                var k = 0;
                while(f[i][j] - f[i-1][j - weights[i]] === values[i] && k < numbers[i]){
                    k++;
                    j -= weights[i];
                }
                totalWeight += weights[i] * k;
                console.log(`选择物品:${k}${i};重量为:${weights[i] * k};价值为:${values[i] * k}`);
            }
        }
        console.log(`背包最大承重:${total};实际重量:${totalWeight};最大价值:${f[n-1][total]}`)
    }
    knapsack([2,3,1],[2,3,4],[1,4,1],6)

闭包

在 js 中闭包可以理解为环境变量的引用。由于 js 的函数执行是用栈进行存储的,所以出栈时,相关函数的环境变量,都会被回收。如果有一些异步操作想要使用该环境变量中的部分变量。就需要使用闭包了。

变量引用不全是闭包

    const foo = () => {
        var a = 1;
        const getA = () => {
            console.log(a);
        }
    }

上述的情况也使用到了外层的环境变量,但是我们并不认为这种情况是闭包。因为在 foo 出栈的时候,a 和 getA 都是内部的环境变量,这个时候外部没有对这两个变量进行持有,都会被回收掉。

真正意义上的闭包

    const foo = () => {
        var a = 1;
        const getA = () => {
            console.log(a);
        }
        return getA;
    }
    
    var fooA = foo();

现在 foo 方法将一个函数返回给了 fooA,所以外部变量对 getA 有了引用,此时 getA 内部的函数调用持有 foo 的环境变量 a 的引用。所以 foo 执行完毕之后,依旧不会释放掉内部的环境变量。

就算我们执行完 fooA() 也不会释放掉 foo 的环境变量。只有当 fooA = null 的时候,getA 发现自己没有被引用了,下一次 gc 的时候才会回收。

闭包优化

上面的流程可以看出来,闭包有好处,可以引用到环境变量,这里用到的词是引用,不是复制,两者会有差别,复制只是多了一个变量,引用将整个环境变量持有。

基于这样就在现有的闭包机制下,优化了环境变量的持有。

    function outer () { 
        var x; // 真正的局部变量
        var y; // context variable, 被inner1使用
        var z; // context variable, 被inner2使用
        function inner1 () { 
          use(y); 
        } 
        function inner2 () { 
          use(z); 
        } 
        function inner3 () { 
          /* 虽然函数体为空,但是作为闭包,依旧引用outer的Context */
        } 
        return [inner1, inner2, inner3];
    }

上述的情况的话,x 是不会被放到闭包的环境变量中的,也就是说真正被引用到的环境变量才会放入一个闭包的机制中。

上述的情况当 inner1 执行完毕之后,也不会立刻释放 y,inner2 也不会立刻释放 z,(y,z,content) 会被一起当做一个闭包变量保存着。

即使 inner3 并没有引用任何变量,但其实是引用了 content 这样的环境变量。

所以只有当 inner1, inner2, inner3 都不再被引用时。才会释放掉这个段闭包。

闭包嵌套

所以,性能上就不要发生闭包嵌套了。这更是一种性能无法忍受的问题