关于javascript:JavaScript-笔记-神奇的闭包

44次阅读

共计 2018 个字符,预计需要花费 6 分钟才能阅读完成。

什么是闭包(Closure)

闭包(Closure)是 Javascript 中一个特地重要的概念,是 JavaScript 有别于很多其余语言的一个神奇个性:对于一个定义好的函数,不管你在哪里调用它,它始终保持着对定义时的上下文的援用。

看一个例子:

function foo() {
    var a = 2;
    function bar() {console.log( a);
    }
    return bar;
}

var baz = foo();
baz(); // 2

示例中,baz 函数不管在哪里被调用,执行后果打印的都是 foo 函数中申明的那个变量 a。你会不会有这个疑难:变量 a 是函数 foo 的一个本地变量,当 foo 执行实现时,a 是不是应该被销毁?

个别状况下,的确是这样的(其余语言也一样,C, Java 都是)。在 JavaScript 中,如果没有造成闭包的话,函数本地变量的确会被销毁,不过示例中变量 a 属于闭包,所以它并不会被销毁。

回到主题,什么是闭包?MDN 对闭包这样定义:一个函数和对其四周状态(lexical environment,词法环境)的援用捆绑在一起(或者说函数被援用突围),这样的组合就是闭包(closure)。

💡 对于 四周状态(词法环境),词法环境是从编译器角度来讲的,对于程序员,能够了解为程序执行上下文,也就是作用域链。

闭包与作用域

再来看个乏味的问题,咱们略微改变一下代码,也就是在上节示例的根底上,定义了另一个本地变量 b,问题是:b 是否在闭包中?

function foo() {
    var a = 2;
  var b = 1; // 定义了另一个本地变量
    function bar() {console.log( a);
    }
    return bar;
}

var baz = foo();
baz(); // 2

咱们看看 Chrome 中的 debug 执行状况:

咱们在 debug 环境下能够看到:b 是不在闭包中的。咱们还看到,以后函数执行时会有一个上下文叫 作用域(Scope),并且其中包含:Local(本地), Closure(闭包), Global(全局) 三类作用域。

看看 ECMAScript 标准对 Scope 的定义:

Table 9 — Internal Properties Only Defined for Some Objects 中,咱们能够看到 [[Scope]] 的 Value Type Domain 一列的值是 Lexical Environment,这阐明 [[Scope]] 就是一种 词法环境,也就是函数执行上下文,作用域链

《你不晓得的 Javascript》上卷中第五章,把 闭包 形容为 作用域闭包,我感觉这是更为精确的形容,更靠近实质。

闭包与函数

再回去看看 闭包 的定义:“一个函数和对其四周状态(lexical environment,词法环境)…”,本文先探讨了 词法环境(作用域),因为我感觉要了解 闭包 ,先要了解 作用域 ,后面也说过,叫 作用域闭包 更能精确形容闭包的实质。

闭包 函数 是什么关系,我认为 函数 闭包 造成的根本环境,这是其一。

而且,这个函数不是全局函数,因为全局函数的词法环境是 Global,而不是 Closure,那么能够这么说:这个函数是一个嵌套定义的函数,这是其二。

其三,咱们能够从上节示例中看出,只有援用了内部函数中定义的变量才会呈现在 闭包 中,也就是说:这个嵌套函数还必须援用内部函数中定义的变量。

另外,闭包 的神奇和弱小还在于:Javascript 中函数是一等公民,能够作为参数、返回值、变量、对象属性值等,也就是 闭包 能够保留和传递。

看一个场景案例(援用自 MDN):

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

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

以这种形式应用闭包,提供了许多与面向对象编程相干的益处 —— 特地是数据暗藏和封装。

总结

最初,咱们能够这样总结:闭包 是一个绑定了内部作用域的嵌套函数。

闭包产生的两个必要条件:

  1. 嵌套定义函数;
  2. 嵌套函数必须援用内部函数中定义的变量;

正文完
 0