编程语言
首页 > 编程语言> > 个人对 JavaScript 闭包的理解

个人对 JavaScript 闭包的理解

作者:互联网

作用域的概念

同级作用域

在一个作用域中声明相同名称的变量会发生变量名冲突的问题。假如在作用域 A 中声明一个变量 a,作用域 B 也声明一个变量 a,两个作用域的变量都互不影响。

// 作用域 A
{
  let a = 0;
  console.log(a);
}

// 作用域 B
{
  let a = 10;
  console.log(a);
}

第一个打印 0,第二个打印 10。

嵌套作用域

作用域是可以嵌套的,作用域 A 嵌套作用域 B,此时两个作用域分别声明变量 a,也是不冲突的。

{// 作用域 A
  let a = 0;
  console.log(a);
  { // 作用域 B
    let a = 10;
    console.log(a);
  }
}

第一个打印 0,第二个打印 10。

在嵌套作用域中,子作用域 B 可以访问父作用域 A 的变量,也可以影响它的父作用域 A。

{
  let a = 0;
  {
    a = 10;
    console.log(a);
  }
}

最终打印的结果是 10。

函数的闭包

JS 的类实际上就是在使用函数的闭包。每执行一次函数就是在生成一个新的作用域,这些新的作用域就像是上面提到的,它们互不影响,只有它们的子作用域可以影响父作用域,即闭包。

闭包缓存数据

function Counter(x) {
  return {
    add: y => {
      return (x = x + y);
    },
    del: y => {
      return (x = x - y);
    }
  };
}

add 和 del 都是父函数 Counter 的子函数,它们之间是一个闭包关系。创建两个 Counter 的实例:

let c1 = Counter(10);
console.log(c1.add(20));
console.log(c1.del(40));

当执行c1.add()时,左边有一个 Closure,说明已经形成了一个闭包:

Closure 中只有 x 是受闭包影响被缓存下来的数据,也就是父函数 Counter 的变量。Local 代表 add 函数自身的变量,没有与其他函数之间(或作用域)形成关系,也就不符合闭包的存在条件,只能由 add 函数自己来使用。

再继续往下执行,可以看到此时 Closure 中的 x 已经是 30:

再往下执行,调用 del 函数,再执行函数的相减操作之间,我们可以看到 Closure 中的 x 还是上一次的结果:30。

再接着往下执行一次,del 函数相减之后,Closure 中的 x 的结果是 -10:

本节小结

受闭包的影响,父函数的数据被缓存下来,子函数可以自由地使用,而且再内存中也不会被销毁。

闭包的好处

仔细观察上面的例子,add 和 del 函数都依赖了相同的变量 x,而这个 x 是父函数给的,再闭包中被缓存起来。add 和 del 只需要传递新的参数就可以参与运算,也就是说,闭包可以减少我们函数的参数传递,使得我们一个计算操作更加连贯,且降低代码耦合度。

假如不使用闭包,通过函数的参数传递来计算,替代上面的闭包函数:

function add(x, y) {
  return (x = x + y);
}

function del(x, y) {
  return (x = x - y);
}

let res = add(10, 20);
let ser = del(res, 40);

就很没有必要,何不如把 x 抽离出来呢?变成一个全局变量:

let x = 10;

function add(y) {
  return (x = x + y);
}

function del(y) {
  return (x = x - y);
}

let res = add(20);
let ser = del(40);

可以,但是不推荐,全局作用域中,变量 x 被声明一次,假如代码越写越多,变量是不是会冲突,代码是不是变得难以维护?闭包可以把 add 和 del 以及 x 都囊括在一个作用域里,也不影响其他的作用域。

本节小节

闭包可以把一块代码容纳在一个里面,形成一个整体,一个不受其他作用域影响的作用域。是不是很像模块开发?没错,我猜测 CommonJS 就是使用的闭包。

总结

  1. 闭包可以让我们使用模块开发思想来写代码,把一系列代码揉进闭包里,是一个有机的结合。类就是使用的闭包,在早期通过闭包来实现模块的开发。

  2. 闭包可以缓存父函数的变量,子函数可以使用,子函数修改父函数的变量,其他子函数也跟着改变。

标签:闭包,函数,作用域,JavaScript,add,理解,del,let
来源: https://www.cnblogs.com/Enziandom/p/16637916.html