发布日期 » 2018年1月27日 星期六

版权声明 » 帅华君原创文章,未经允许不得转载。

好好说话,好好解释闭包

今天我们一起来谈论JavaScript中的闭包,不过你要做好心理准备,因为闭包有非常多难懂的知识点,我也很困惑为什么有些人会JS小白编一些很难理解的例子来讲解闭包。所以今天我将用最简单的例子来让你清楚的理解闭包。我打赌你看完这篇文章会对JavaScript中的闭包有基本的理解。

就如在大多数编程语言中一样,创建一个函数,并在函数内声明了形参和一些内部变量,就如以下代码片段:

var addTo = function(passed){
  var inner = 2;
  return passed + inner;
}

console.log(addTo(3));

如上,声明一个变量名叫做 addTo 的函数,该函数接收一个参数,在函数内部这一参数变量名叫做 passed 。并且还在函数内部声明一一个变量名叫做 inner 的变量,并给它赋值为2。最后将变量 passedinner 两个值相加后的结果返回,返回值也就是该函数的值。当我们调用该函数,并为它传入数字3,显然 3+2 结果是 5,所以控制台会输出 5

如果我们将上面的代码片段进行一些修改:

var passed = 3;

var addTo = function(){
  var inner = 2;
  return passed + inner;
}

console.log(addTo());

我们在函数 addTo 的外部作用域声明了一个变量名为 passed 值为 3 的变量。并且将函数 ***addTo***修改为不需要传任何参数,但是 addTo 函数中依然需要用到一个叫做 passed 的变量,最后执行 console.log() 方法(显然,函数未定义形参,所以也不需要再传入实参了),在控制台输出的结果依然是 5

看起来一切都再正常不过了,看似执行结果也满足我们的期望。当时上面的代码其实创建了一个 闭包环境 ,也许看起来和你曾经见过的 函数嵌套函数 看起来不一样(因为我不希望你一开始就被多层嵌套关系吓住而失去学习的信心),不过这种情况的确是创建了一个闭包,一个非常简单的闭包。

下面你将再看另一个比较复杂的创建闭包环境的例子。

在JavaScript中,在函数外部创建的变量,会自动在函数内部可以访问到,因为JavaScript使用一种叫做 词法作用域 的机制,表现出来的规则就是:外部无法访问内部变量,而内部可以访问外部定义的变量。而实现这一机制和规则的就是 闭包

有一次一个家伙让我解释什么是闭包,我把上面的两则代码片段告诉他,他说不,这不是闭包,他觉得内部函数内部又声明了一个函数的那才叫闭包。当然,是的,在函数内部创建一个函数,并且在内部的函数中使用到了该内部函数外的一些变量,也会创建闭包环境。但是上面两则代码片段的确也能称得上是 闭包 ——最简单的闭包。

理论上,任何直接使用外部变量的函数都称得上是闭包(这也是为什么多种代码规范中会提到多写 纯函数 避免不必要的闭包的产生,纯函数就是通过参数获取外部变量,并且如果传入的是引用值,比如数组、对象等,则不应该直接在该变量上进行操作),你完全可以认为一个JavaScript文件在浏览器中被解释执行时,是放在一个函数中执行的。这样想的话,全局中声明的函数,其实就类似于在函数中声明的函数。因为 全局局部 的概念总是相对的。

如果你还是觉得在全局变量中声明的函数,就算函数内部使用了外部变量,也称不上是闭包的话,没关系,继续往下看。

我们知道,数组、对象、函数,他们三者在JavaScript中本质上都是对象,对象就是包含一些属性和方法的集合。既然在JavaScript中函数也是对象,我们不妨将函数展开,看看它都有那些属性和方法:

清楚的看到,addTo 函数作用域链上存在一个闭包,闭包中存储的就是在全局中定义的变量 passed

下面给出一则公认的闭包案例:

var addTo = function(passed){
  var add = function(inner){
    return passed + inner;
  }
  return add;
}
console.dir(addTo(1));

将上方代码执行可以发现,内部函数 add 在函数返回后依然可以访问到为 addTo 传入的参数。

希望你看完这篇文章能对闭包有全新的认识。

本篇完 ·END·