JS:关于闭包

闭包

对闭包的理解

  1. 闭包是有权访问另一个函数作用域中的变量的一个函数。
  2. 简单来说就是定义在一个函数内部的函数。
  3. 闭包可以访问到父级函数的变量,且该变量不会销毁。

闭包的原理

利用了作用域链的特性,我们都知道作用域链就是在当前执行环境下访问某个变量时,如果不存在就一直向外层寻找,最终寻找到最外层也就是全局作用域,这样就形成了一个链条

闭包的体现

闭包只能取得包含函数中的任何变量的最后一个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function person (){
var age = 18;
function cat(){
age ++;
console.log(age);
}
console.log(age)
return cat;
}
person() // 18
var p = person(); // p相当于函数cat
// 调用时会先在cat函数作用域内查找变量age,没有则顺着作用域链往上查找,找到person中的age
// 经过首次调用后cat中存在age变量,这样每次调用都不经过age的初始值,就可以一直增加
p() // 19,p()即cat()
p() // 20
person() // 18,每次调用person函数,进入该作用域,age都会重新赋值为18

闭包的作用

  • 隐藏变量,避免全局污染
  • 可以读取函数内部的变量

闭包的缺点

  • 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。
  • 闭包会导致局部变量能在函数作用域之外被访问,但是这不是变量不可被回收的充分条件,通常情况下是考虑局部变量是否被全局或者一个不可被回收的对象引用了。
  • 闭包被引用后始终在内存中,不会在调用结束后被垃圾回收机制回收,可能会造成内存消耗。
    1
    2
    // 释放闭包引用
    p = null;

闭包的应用场景

封装变量

为避免全局污染,把变量封装到函数作用域中

循环绑定事件处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var lis = document.getElementsByTagName('li')

// 每循环一次给lis添加一个function,最后lis是这样的 lis[function(){},function(){},function(){}]
// 里面有3个function,在for里面声明了function,但还没有立即执行
// 真正执行的时候是当点击li的时候才执行这个函数// 当执行的时候for循环已经结束,i已经变成最后一个取值了,所以每触发一次事件都访问的是等于最后一个取值的i

for(var i=0;i<lis.length;i++){
lis[i].onclick = function(){
console.log(i);
};
}

// 立即执行函数要保护j这个索引值的变量不被污染
for(var i=0;i<lis.length;i++){
(function(j){
lis[j].onclick = function(){
console.log(j);
};
})(i); //事件处理函数中闭包的写法
}

循环打印

1
2
3
4
5
6
7
8
9
10
11
// 定时器中的回调函数是一个在立即执行函数中的闭包,单独的立即执行函数能不能叫做闭包说法各有不同。
function fn(){
for(var i = 1;i <= 5;i ++){
(function(j){
setTimeout(function () {
console.log(j)
}, 1000)
})(i);
}
}
fn() // 1 2 3 4 5

拓展

垃圾回收机制

标记清除

工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

工作流程:

  1. 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
  3. 再被加上标记的会被视为准备删除的变量。
  4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

引用计数

工作原理:跟踪记录每个值被引用的次数。

工作流程:

  1. 声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。
  2. 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.
  3. 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.
  4. 当引用次数变成0时,说明没办法访问这个值了。
  5. 当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。

引起内存泄漏

  1. 全局变量
  2. 闭包
  3. 原因:都是由于不能被回收
-------------本文结束 感谢您的阅读-------------