JavaScript 闭包

匿名 (未验证) 提交于 2019-12-02 21:53:52

这篇算接上一篇:【JavaScript 预编译:函数声明提升,变量声明提升

这两篇都是因为看了这个视频,觉得讲的挺清楚的就记录下来了


说闭包之前我们先介绍几个概念:

当函数执行时(实际上是函数执行前一刻)会创建一个称为执行期上下文的对象(就是上一篇介绍的 Activation Object),一个执行期上下文定义了一个函数执行期间的环境。当函数执行完毕,它所产生的执行期上下文被销毁。

函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文。

每个 JavaScript 函数都是一个对象,对象中有些属性我们可以访问,但有些不可以。不可以访问的这些属性仅供 JavaScript 引擎存取,[[scope]] 就是其中一个。

[[scope]] 指的就是我们所说的作用域,其中存储了执行期上下文的集合。

[[scope]] 中所存储的执行期上下文对象的集合呈链式连接,我们把这种链式连接叫做作用域链。查找变量就是从作用域链的顶端依次向下查找。


下面来看一个例子:

function a() {     function b() {         var b = 234;         console.log(b);     }     var a = 123;     b();     console.log(b); } console.log(a); var glob = 100; a(); 
  1. 函数 a 被定义时发生如下过程:(此时 a 在全局的执行环境中,a 的作用域链上只有一个 GO 对象)
    其中 Global Object 中还应该有 window,document 等对象,这里暂且不管,下同。

  2. 函数 a 执行时发生如下过程:(a 执行的前一个会创建一个它自己的执行上下文 AO 对象,并且将这个对象放在作用域链的最顶端)
    其中 Activation Object 中还应该有 arguments 等,这里暂且不管,下同。

  3. 函数 b 被定义时发生的过程跟函数 a 执行时发生的过程是一样的,此时 b 在 a 的执行环境中。(此时 b 的作用域链上的 AO 对象跟 a 的作用域链上 AO 对象是同一个,都是 a 的)

  4. 函数 b 执行时发生的过程如下:(b 执行的前一个会创建一个它自己的执行上下文 AO 对象,并且将这个对象放在作用域链的最顶端,其他对象依次下移)

当函数 a 函数 b 执行完成后,他们所产生的执行期上下文全部都会被销毁。


再看一个例子:

function a() {     function b() {         var bbb = 234;         console.log(aaa);     }     var aaa = 123;     return b; } var glob = 100; var demo = a(); demo(); 
  1. 函数 a 被定义时发生如下过程:

  2. 函数 a 执行时发生如下过程:

  3. 函数 b 被定义时发生的过程跟函数 a 执行时发生的过程是一样的。

  4. 函数 demo/b 执行时发生的过程如下:
    值得注意的是,在函数 a 执行完成后将函数 b 作为返回值赋给了全局变量 demo。函数 a 执行完成后会销毁自己的执行期上下文,但是被保存到外部的函数 b 的作用域链上还是能够访问到 aaa。所以当 demo 执行的时候也就是 b 执行的时候,能够输出 aaa 的值 123。 这就是我们说的闭包。


当内部函数被保存到外部时,仍然能够访问到原来包含函数内部的变量,就会形成闭包。
闭包会导致原有作用域链不释放,造成内存泄漏。(内存泄漏就是,内存被占用的越来越多,可供使用的越来越少。)

闭包为什么会造成内存泄漏?(这个纯粹是我个人的理解,不知道对不对)
js 垃圾收集机制的原理是找出那些不再继续使用的变量,释放其占用的内存。
按照上面的例子来说,函数 b 执行完成之后本应该释放其作用域链,但是由于函数 b 总是被 变量 demo 引用,所以 js 就认为函数 b 一直是有用的,一直不回收它。因此造成了内存泄漏。

  1. 实现公有变量;eg:函数累加器
  2. 可以做缓存(存储结构);eg:eater
  3. 可以实现封装(属性私有化);eg:Person
  4. 模块化开发(防止污染全局变量)

前两个作用对应的例子:

eg:函数累加器

function add() {     var count = 0;     function fnCount() {         count++;         console.log(count);     }     return fnCount; } var counter = add(); counter(); counter(); counter(); counter(); counter(); ...  或者  var counter = null;  function add() {     var count = 0;     counter = function() {         count++;         console.log(count);     } } add(); counter(); counter(); counter(); counter(); counter(); ... 

eg:eater

function eater() {     var food = '';     var handFood = {         eating: function() {             if(food){                 console.log('I am eating ' + food);             }             else{                 console.log('Nothing to eat!');             }         },         pushFood: function(myfood) {             food = myfood;         }     };     return handFood; } var myEater = eater(); myEater.eating(); myEater.pushFood('banana'); myEater.eating(); 
文章来源: JavaScript 闭包
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!