闭包

当程序调用一个函数时,会发生什么?

1JavaScript创建一个新的执行上下文,我们叫作本地执行上下文。

2这个本地执行上下文将有它自己的一组变量,这些变量将是这个执行上下文的本地变量。

3新的执行上下文被推到到执行堆栈中。可以将执行堆栈看作是一种保存程序在其执行中的位置的容器。

函数什么时候结束?当它遇到一个return语句或一个结束括号}

当一个函数结束时,会发生以下情况:

1这个本地执行上下文从执行堆栈中弹出。

2函数将返回值返回调用上下文。

3这个本地执行上下文被销毁,销毁是很重要,这个本地执行上下文中声明的所有变量都将被删除

因此引出变量的作用域

1. 变量的作用域

根据作用域的不同,JavaScript 中的变量可分为两种:全局变量局部变量

其中,函数内部可以直接读取全局变量

  • 全局执行上下文未删除

但是,函数外部不能读取函数内的局部变量

2. 什么是闭包

以下摘自MDN:

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

仅函数存在闭包,对象不存在闭包(访问不到词法环境)

function fn1() {
    let a = 1;
    return function fn2() {
        console.log(a);
    };
}
// 因为fn1函数有返回值,用result接收
const result = fn1(); // result存储函数定义和闭包(a=1)
result(); // 1

//仅函数有闭包,对象没有
var a = {
    name : "Cherry",

    func1: function () {
        console.log(this.name)     
    },

    func2: function () {func1()//func1 is not defined
        setTimeout( () => {
            this.func1()//cherry
			
        },1000);
    }
};

a.func2()

1.无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包闭包包含在函数创建时作用域中的所有变量,它类似于背包。函数定义附带一个小背包,它的包中存储了函数定义创建时作用域中的所有变量。

function makeAdder(x) {
    return function (y) {
        return x + y;
    };
}

const add5 = makeAdder(5);//
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12

个人理解:执行makeAdder函数时创建一个新的本地执行上下文,里面含变量x=5和函数function,执行完后将function(y)返回,之后执行上下文销毁,但add5的实例维持了对它语法环境的引用(x存在其中),故add5调用时,变量x仍可用

add5add10都是闭包。它们共享相同的函数定义,但是保存了不同的词法环境。在add5的环境中,x为 5。而在add10中,x则为 10。

3. 闭包的作用

1. 在函数外部访问到函数内部变量

2. 使变量留存在内存中

闭包另一个作用,使已经运行结束的函数上下文中的变量对象继续留在内存中。看下面一段 demo:

function fn1() {
    var a = 1;
    return function fn2() {
        console.log(a++);
    };
}
var result = fn1();
result(); // 1
result(); // 2
result(); // 3

上述代码执行 3 次result()后,分别输出了a的值,a不断地+1。这说明函数fn1中的变量a一直保存在内存中,并没有在函数fn1调用后被清除。

因为函数fn2被赋给了一个全局变量,因此fn2会一直在内存中,fn2保存了fn1的语法环境,故a并不会被

垃圾回收机制清除。

3. 模拟私有方法

const counter = function () {
    let privateCounter = 0;

    return {
        add() {
            privateCounter++;
        },
        min() {
            privateCounter--;
        },
        value() {
            return privateCounter;
        },
    };
};

const counter1 = counter();
const counter2 = counter();

counter1.add();
counter1.add();
counter1.add();

counter2.min();

console.log(counter1.value()); // 3
console.log(counter2.value()); // -1

两个计数器counter1counter1都保持各自的独立性。每个闭包都是引用自己作用域内的变量privateCounter,每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。另外,在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。

4. 闭包的副作用

1. 常驻内存

闭包会使得函数中的变量都被保存在内存中,内存消耗很大

详细闭包案例:我从来不理解JavaScript闭包,直到有人这样向我解释它

参考

理解闭包

我从来不理解JavaScript闭包,直到有人这样向我解释它

阅读剩余
THE END