前端小白如何理解闭包

前言
假设阅读本文的你已经具备初级前端基础,了解作用域链执行环境全局变量活动变量等概念。(此处很想放链接,但是我手残还没写相关的解释文章,童鞋们自己搜一下吧)

先定义:

闭包是指:有权限访问另一个函数作用域中的变量的函数。

而要想了解闭包的概念,首先要对函数的执行流程有一定的了解。

  • 函数的执行过程

以该函数为例:

function test (i, j) {
  return i + j;
}
var result = test(1, 2);

首先,在创建test()函数时,就会生成一个包括全局变量对象的初始作用域链,这个作用域链保存在test()函数内部的[[scope]]属性中。
test()函数被调用时,会创建一个执行环境和相应的作用域链(通过复制test()函数的[[scope]]属性中的值来构建),此后,会创建一个包含arguments, i, j的活动对象,这个活动对象会推入到作用域链的前端。
最后,一般来讲,当函数执行完毕之后,局部的活动对象就会被销毁,内存中只保留全局的变量对象。
这段话看起来比较绕口,但是配合下面这张图看一下,瞬间会感觉特别清晰。

函数调用时的作用域链

  • 闭包

以上是一般的函数执行过程,❗但是对于闭包来讲,情况是稍微不同的❗,以如下函数为例:

function newTest (propertyName) {
  return function(obj1, obj2) {
    var val1 = obj1[propertyName];
    var val2 = obj2[propertyName];
    return val1 + val2;
  }
}

PS: newTest()函数内部创建了一个匿名函数,而匿名函数和闭包实际上是两个不同的概念。只是创建闭包的常见方式是通过匿名函数实现。

前面我们已经知道:闭包是有权限访问另一个函数作用域中的变量的函数

在上面的函数中,可以看到:

    var val1 = obj1[propertyName];
    var val2 = obj2[propertyName];

val1val2是访问了外部函数变量propertyName,而之所以val1val2可以访问到newTest()中的变量是因为,匿名函数的作用域链中包括了newTest()的作用域。所以,newTest()内部的匿名函数的作用域链中,实际上会包含newTest()的活动对象。

所以,当调用匿名函数时:

// 创建函数
var x = newTest('num');
// 调用函数
var result = x({num: 1},{num: 2}); // result: 3

匿名函数从newTest()被返回后,它的初始作用域链就会包括:①全局变量对象、②newTest()的活动对象。而且在newTest()被返回后,newTest()的执行环境的作用域链会被销毁,但newTest()的活动对象仍然会留在内存中,直到匿名函数被销毁后,newTest()的活动对象才会被销毁。例如:

// 解除匿名函数的引用,才会释放内存
x = null;

或许看文字还是容易混乱,配合图片理解更下饭:


调用闭包时的作用域链

由于闭包会包括其他函数的作用域链,因此闭包会比其他函数更占内存,建议慎重使用。

✍后记:
本文知识点主要来源于红宝书,此篇也算是自己的笔记吧~
如果有小蒙理解错误的地方,请各位指点一下,感激不尽!❤❤❤

你可能感兴趣的:(前端小白如何理解闭包)