闭包

闭包(Closure)

它与 JavaScript 中的垃圾回收(每门语言的垃圾回收是不一样的)有点联系

什么是环境呢?

就比如一个 城市,在城市里面,我们会有很多设施:公园、医院、学校、商业广场...,那么这些设施产生了之后,就构成了这个城市的环境

那么这些环境存在的依据是什么,那是有人生活在这个城市,如果没有人生存在这里了,那么这个城市就会被摧毁掉,要么人为摧毁,要么历经日晒雨淋被摧毁。

反正就是如果没有人住了,这个环境就没有存在的意义了,那么它就应该被摧毁。

同样的,在程序中也是这样的道理,环境存在的依据是被需要

还有要注意的一点,环境里面的设施,诸如公园、医院,它也是有作用范围的,它的作用范围就只有那么大,那么这个作用范围就可以理解为程序中的作用域

我们来看一下这个例子

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
    <style> </style>
</head>
<body>

<script>
    let name = 'jack'
    console.log(name)
</script>
</body>
</html>

这里呢,就有一个全局环境(这里 name 就相当于上面所说的 城市里面的设施公园、学校等)

那么我这里输出了 name 之后,name 这个变量它会不会被删掉呢?

不会,这主要取决于它后续是否会被用到,因为我们这里是一个 HTML 页面,控制台有可能随时访问这些变量,所以不会,除非你把这个页面关了

环境,在计算机当中,就是给我们开辟了一块内存

让我们再来看一个例子

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
    <style></style>
</head>
<body>
    
<script>
    let name = 'jack'
    function sayHello(){
        let word = 'hello world'
        console.log(word)
    }
    sayHello()
</script>
</body>
</html>

在 js 当中,函数也是有自己的环境的

但是这个环境,要注意了,只有在这个函数执行的时候,它才有这个环境,计算机才会给你开辟一个内存空间

默认情况下,外部的环境是无法访问到函数内部环境的变量的,因为函数内部变量的作用域就只是在这个函数里面

所以,当我们把这个函数执行完了,该函数里面的变量就没人能够用到了是吧?所以这个环境,即这片内存,就会被清理掉

因为上文我们说到,当环境不被需要,就会被清理掉

还有一个要注意的地方

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
    <style></style>
</head>
<body>

<script>
    let name = 'jack'
	function sayHello(){
        let word = 'hello world'
        console.log(word)
    }
    sayHello()
    sayHello()
    sayHello()
</script>
</body>
</html>

函数每调用一次,就会开辟一次内存空间,每一次都是一个全新的内存空间

上面例子中,函数执行了三次,那么就是开辟了三次内存空间

闭包的使用

实现累加

function test() {
	let n = 1
	function sum() {
		console.log(++n);
	}
	sum()
}
test()
test()
test()

那么这个例子可以累加吗?

不能,因为每一次函数的调用,都是开辟了一个环境,即一片新的内存,并且这片内存执行完,就会被清理

function test() {
    let n = 1
    function sum() {
        console.log(++n);
    }
    return sum
}
let a = test()
a()
a()
a()

我们可以看出,test() 函数的返回值赋值给了变量 a

相当于把 sum 函数一直被 a 用着

在计算机当中,只要这个数据在被使用,那么就不会被清理

相应的,sum 函数所在的环境中所有的变量都不会被清理,注意不是 sum 函数里面的变量,而是 sum 函数所在的环境的变量

我们再来看一个例子

function test() {
    let n = 1
    function sum() {
        console.log(++n);
    }
    return sum
}
let a = test()
a()
a()
a()
let b = test()
b()
b()
b()

这个是因为 函数执行之后 都创建了一块新的内存空间

还有一个例子

function one() {
    let n = 1
    function two() {
        let m = 1
        function three() {
            console.log(++n);
            console.log(++m);
        }
        return three
    }
    return two
}
let a = one()()
a()
a()
a()

可以看出 n 也是保留的

构造函数中使用闭包

function Person() {
    let n = 1
    this.sum = function () {
        console.log(++n);
    }
}
let p = new Person()
p.sum()
p.sum()
p.sum()

只要没有被清理就会形成闭包,这个例子跟下面这个例子是一样的

function Person() {
    let n = 1
    function sum(){
        console.log(++n);
    }
    return {sum}
}
let p = new Person()
p.sum()
p.sum()
p.sum()

引用数据类型 才能形成闭包,基本数据类型 不行

例如

function test() {
    let n = 1
    function add() {
        console.log(++n);
    }
    return {add}
}
let a = test()
a.add()
a.add()
a.add()

可以用闭包封装私有变量

function Person() {
    var age = 0
    this.getAge = function () {
        return age
    }
    this.addAge = function () {
        age++
    }
}
const p1 = new Person()
const p2 = new Person()
p1.getAge()
p2.getAge()

这样外部就无法直接访问变量 age,只能通过闭包访问,我们就近乎实现了面向对象语言中私有变量的效果

总结

闭包的形成必备条件:😕

该内存没有被清理,被外部引用着

并且是有一种物理嵌套的关系(函数里面套函数、函数里面返回对象也行)

闭包的作用:

使得一个外部函数有权访问一个内部函数的作用域

可以说,闭包让函数的生命周期变长了

使用闭包的注意点:

1、虽然闭包非常有用,但是在使用时,它里面的信息会一直存储在内存中,只有 JavaScript 引擎确保这些信息不在使用时,才会清理这些信息。你可以在使用完毕之后,手动赋值为null,这就是手动回收

2、在 IE 中,闭包会导致内存泄漏(是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来),这是 IE 的 BUG,因为在 IE 中,当我们使用完闭包之后,它会出现回收不了闭包里面的变量。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

相关链接:

https://www.bilibili.com/video/BV1YJ411R7ap?p=3&share_source=copy_web

posted @ 2022-11-10 20:48  朱在春  阅读(15)  评论(0编辑  收藏  举报