1.4.7 闭包

闭包是 JavaScript 最强大的特性之一。 JavaScript 允许函数嵌套,对于在外部函数中定义的和外部函数可以访问的所有变量和函数,内部函数具有完全的访问权限。 而外部函数却没法访问在内部函数中定义的变量和函数,这样就为内部函数的变量提供了一种封装。

此外,因为内部函数可以访问外部函数的作用域(链),所以如果内部函数能够在外部函数的生命周期之外继续存在, 那么在外部函数中定义的变量和函数的存在周期就会比外部函数本身的执行时间更长。 当内部函数以某种方式能在外部函数之外的任何作用域中可用时,就会创建一个闭包。

比如:

// 外部函数有个参数 name
const pet = function(name) {
  const getName = function() {
    return name; // 内部函数可以访问外部函数的变量 name
  };
  return getName; // 返回内部函数,以此将它暴露给外部作用域(outer scopes)
};

const myPet = pet("Vivie");

myPet(); // 返回 "Vivie"

再复杂点的例子,如下:

// 外部函数的变量一:函数参数 name
const createPet = function(name) {
  let sex; // 外部函数的变量二:声明的变量 sex

  // 以下内部函数来操作和访问这两变量
  const pet = {
    setName(newName) {
      name = newName;
    },
    getName() {
      return name;
    },
    getSex() {
      return sex;
    },
    setSex(newSex) {
      if (
        typeof newSex === "string" &&
        (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
      ) {
        sex = newSex;
      }
    }
  };

  return pet; // 返回一个对象,包含一系列内部函数(也能将它们暴露给外部作用域)
};

const pet = createPet("Vivie");
pet.getName(); // Vivie
pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName(); // Oliver

在上面的代码中,要想访问外部函数的 name 变量和 sex 变量,只能通过内层函数来访问。 内部函数的内部变量充当外部参数和外部变量的安全存储,它们保存“持久”和“封装”的数据供内部函数使用。 这些函数,甚至都不需要赋值给变量,也不需要有名字,如下:

const getCode = (function() {
  const apiCode = "0]Eal(eh&2"; // 不希望外部修改的代码

  return function() {
    return apiCode;
  };
})();

getCode(); // 返回 apiCode

注意,使用闭包的陷阱。

如果内嵌函数中定义的变量,和外部作用域里的一个变量同名了,那就没办法引用到外部作用域中的变量了。如下:

// 外部函数的变量:函数参数 name
const createPet = function(name) {
  return {
    // 内部函数也定义了个同名的变量 name
    setName(name) {
      name = name; // 这样我们就访问不到外部函数定义的 name 了
    }
  };
};

因为内部作用域的同名变量会“覆盖”外部的,直到程序退出内部作用域,详见命名冲突。

Last updated