[TOC]

原文链接:http://javascript.info/closure,translate with ❤️ by zhangbao.

JavaScript 是一种非常面向函数的语言,它给了我们很大的自由。一个函数可以在某一时刻被创建,然后复制到另一个变量,或者作为参数传递给另一个函数,然后从一个完全不同的地方调用。

我们知道一个函数可以访问外部变量,这个特性经常被使用。

但是当外部变量发生变化时会发生什么呢?一个函数能否得到最新的值,或者当函数被创建时存在的值?

另外,当一个函数移动到代码中的另一个地方,并从那里调用它时,会发生什么——它是否能访问新地方的外部变量?

不同的语言在函数行为表现上也是不同的,在这一章里,我们仅讨论函数在 JavaScript 的行为。

几个问题

让我们先来考虑两种情况,然后逐个研究内部机制,这样你就能在未来回答以下问题和更复杂的问题。

  1. 函数 sayHi 使用了外部变量 name。当函数执行时,那个值是它使用的?
let name = 'John';

function sayHi() {
  alert('Hi, ' + name);
}

name = 'Pete';
sayHi(); // 显示的是“John”还是“Pete”呢?

这种情况在浏览器和服务器端开发中都很常见,一个函数可能会被安排在比它创建的时间稍晚些的时候执行,例如在用户操作或网络请求之后。

所以,问题就变成:它是否能获得最新的值?

  1. 函数 makeWorker 返回了另一个函数。新函数在一个别的地方被调用,它还能访问到创建地方的外部变量吗,或者是调用地方的外部变量,或者都可以?
function makeWorker() {
  let name = "Pete";

  return function() {
    alert(name);
  };
}

let name = "John";

// 创建函数
let work = makeWorker();

// 调用它
work(); // 会显示什么呢?“Pete”(创建地方的)或者是“John”(调用地方的)

词法环境

为了理解发生了什么,让我们首先讨论一个“变量”到底是什么。

在 JavaScript 中,每个运行中的函数、代码块、整个脚本都会有一个与之关联的称为 词法环境 的对象。

词法环境对象包含两部分:

  1. 环境记录:一个将本地变量作为属性记录下来的对象(还有一些其他的,像 this 这样的信息)。

  2. 一个指向 外部词法环境 的引用,通常是在词法上的直接外部代码(也就是花括号外面的)。、

因此,“变量”就是一个特殊内部对象——环境记录上的一个属性。“获得或者改变变量”表示“获得或者修改词法环境上的一个属性”。

举一个简单的例子,当前只有一个词法环境:

闭包 - 图1

这就是所谓的全局词法环境,与整个脚本关联的。对浏览器环境而言,所有的