深入理解js闭包

深入理解js闭包

在面试中遇到两个闭包的题目:

let total = 0;
let result  = [];
let a = 3;
function foo(a){
	let i = 0;
    for(;i<3;i++){
        result[i] = function(){
            total += i*a; 
            console.log(total);
        }
	}
}
foo(1);
result[0]();
result[1]();
result[2]();

手写代码,实现单次执行a()就输出1;偶次执行a()就输出2;不可用全局变量;
a() ; //1
a() ; //2
a() ; //1
a() ; //2

第一题的答案是:3,6,9;执行foo(1)的时候,传入的数据a = 1;执行for循环,i最终变成了3;result里面存放的是一个个function;当执行时,i和a始终是1和3;

第二题的思路:用一个标识符i来标示执行的次数;用(i%2)来判断奇偶,由于i不能是全局变量,为了保存住i的值,所以要用到闭包;

最初学习闭包的时候,是从AO、GO的角度去理解闭包的,但是这样理解方式是ES3的,已经不能解释现在很多语法了。

在学习闭包之前,要明白两个知识点:作用域、作用域链

作用域

变量作用域两种: 全局变量和局部变量

  1. 函数内部可以直接读取全局变量
  2. 函数外部无法读取函数内部的局部变量

作用域链

简单说就是作用域集合 ,当前作用域 -> 父级作用域 -> … -> 全局作用域 形成的作用域链条 。他保证了变量对象的有序访问。

闭包的概念

闭包就是能够读取其他函数内部变量的函数,函数没有被释放,整条作用域链上的局部变量都将得到保留 ; 闭包其实就是将函数内部和函数外部链接的一座桥梁

特性
  1. 闭包的外层是个函数,闭包内部有函数
  2. 闭包会return内部函数,闭包返回的函数内部不能有return
  3. 执行闭包后,闭包的内部变量会存在,闭包内部函数的内部变量会回收

闭包好处和用途

好处:

  1. 希望一个变量长期驻扎在内存中
  2. 避免全局变量污染

用途: 匿名的自执行函数 (封装工具类的时候可以运用)、对数据进行缓存

注意事项

滥用闭包,会造成内存泄露的问题;内存泄露的意思就是变量无法正常地从内存里面释放掉。

垃圾回收机制

垃圾回收的两种实现方式垃圾回收有两种实现方式,分别是标记清除和引用计数
标记清除
当变量进入执行环境时标记为“进入环境”,当变量离开执行环境时则标记为“离开环境”,被标记为“进入环境”的变量是不能被回收的,因为它们正在被使用,而标记为“离开环境”的变量则可以被回收;

function func3 () {
      const a = 1
      const b = 2
      // 函数执行时,a b 分别被标记 进入环境
}

func3() // 函数执行结束,a b 被标记 离开环境,被回收

引用计数统计

引用类型变量声明后被引用的次数,当次数为 0 时,该变量将被回收

function func4 () {
      const c = {} // 引用类型变量 c的引用计数为 0
      let d = c // c 被 d 引用 c的引用计数为 1
      let e = c // c 被 e 引用 c的引用计数为 2
      d = {} // d 不再引用c c的引用计数减为 1
      e = null // e 不再引用 c c的引用计数减为 0 将被回收
}

但是引用计数的方式,有一个相对明显的缺点——循环引用

function func5 () {
      let f = {}
      let g = {}
      f.prop = g
      g.prop = f
      // 由于 f 和 g 互相引用,计数永远不可能为 0
}

像上面这种情况就需要手动将变量的内存释放

f.prop = null
g.prop = null

你可能感兴趣的:(前端)