三十四.闭包和内存管理机制和内存泄漏

96 阅读4分钟

什么是闭包

闭包就是指在一个作用域中可以访问另一个函数作用域中的内部变量的函数。

闭包通常是在嵌套函数中实现的,闭包的实现是利用了JavaScript中作用域链的概念,在JavaScript中,如果在某个作用域下访问某个变量的时候,如果不存在,就一直向外层寻找,直到在全局作用域下找到对应的变量为止,这里就形成了所谓的作用域链。

image.png

tool这个作用域下访问了另外一个函数apply下的局部变量money

image.png

闭包的使用场景

任何闭包的使用场景都离不开这两点:

  • 创建私有变量
  • 延长变量的生命周期

一般函数执行时所创建的执行上下文对象(AO对象)会在函数返回后这个函数的作用域就会就被销毁而创建的AO对象也会被销毁,但是闭包会保存创建的AO对象的引用,即便函数返回后这个这个函数的作用域被销毁了但创建的执行上下文对象也不会被销毁,以达到延长变量的生命周期的目的

  1. 创建私有变量,延长生命变量周期
function person() {
  var age = 18;
  return function(){
    age++;
    console.log(age);
  }
}

let getPersonAge = person();
getPersonAge(); // 19
getPersonAge(); // 20
getPersonAge(); // 21

每当调用getPersonAge()函数的时候,首先要获取age变量,因为JavaScript中存在作用域链的关系,所以会从person函数下得到对应的age,因为闭包存在着闭包可以访问到父级函数的变量,且该变量不会销毁的特性所以上次的变量会被保留下来。

  1. 利用函数自调用,每次调用内部的局部变量或者形参都是独立的来保存一些临时数据。如:for循环内部如果有延时获取for循环中初始条件的变量的情况,由于for循环不具有作用域则for循环中初始条件的变量取值时不真实可用闭包解决。

image.png

image.png

image.png

image.png

  1. 利用函数的独立作用域来生成业务代码块,内部的变量相互不冲突这样不会污染全局变量。

image.png

image.png

  1. 可以把一个函数封装在原型中,这样每一个用这个函数创建的对象都可以共用这个函数,这个函数就是闭包。

使用闭包来定义公共函数,并让这个公共函数可以访问私有函数和变量:

Countervar Counter = function(){
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function(){
      return changeBy(1);
    },
    decrement: function(){
      return changeBy(-1);
    },
    getValue: function(){
      return privateCounter;
    }
  }
}

var counterInstance1 = Counter();
var counterInstance2 = Counter();
// c1 计数器1
console.log(counterInstance1.getValue()); // 0
counterInstance1.increment();
counterInstance1.increment();
counterInstance1.increment();
console.log(counterInstance1.getValue()); // 3
counterInstance1.decrement();
console.log(counterInstance1.getValue()); // 2

// c2 计数器2
console.log(counterInstance2.getValue()); // 0
counterInstance2.increment();
counterInstance2.increment();
console.log(counterInstance2.getValue()); // 2
counterInstance2.decrement();
counterInstance2.decrement();
counterInstance2.decrement();
console.log(counterInstance2.getValue()); // -1

闭包的优缺点

1)闭包的优点:利用闭包使函数内部的变量能被外部直接使用;一些临时数据希望延时业务中使用可以利用闭包把临时数据保存到局部作用域中;防止全局变量污染可以用闭包把一些业务变量放在局部作用域中。

2)闭包缺点:闭包使用不恰当会导致内存泄漏,解决方案:尽量避开不使用闭包或者在可能存在泄露的地方把标识符引用为null。

image.png

内存泄漏

浏览器运行网页就会执行js代码,引用数据会在内存中占用内存空间。即内存泄漏是有一个对象创建了且占用了内存空间却没有任何业务能够访问使用。

比如:局部作用域销毁了但是外部作用域可间接访问局部作用域的对象则这个对象就可以被使用。因此函数调用完毕之后本该释放的对象内存空间由于外部作用域还在内部这个对象可以被使用,内部的对象就不会释放而后面的代码没有再操作过内部的这个对象,内存会一直占用但又未被使用就导致了内存泄漏。

image.png

内存管理机制

垃圾回收机制和引用计数机制是底层浏览器的代码实现的功能。

系统会定期查看js执行情况,观察创建的对象有没有可能会被使用,如果没有可能被使用就释放内存。

image.png

引用计数:每一个对象被标识符引用就会引用计数一次,当引用计数为0就释放内存。