JavaScript 系列 - 闭包

87 阅读1分钟

概念

闭包(closure)是一个函数以及其捆绑的周边环境状态(词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。闭包会随着函数的创建而被同时创建

  • 创建私有变量
  • 已经运行结束的函数上下文中的变量对象继续留在内存中

使用

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}
// 因为 displayName 创建时作用域里面已经有 name
var myFunc = makeFunc();
myFunc();

用闭包模拟私有方法

返回对象里面的函数引用函数外部的变量

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 */

在循环中创建闭包

function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];
  // 错误
  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}
setupHelp();
// 闭包
function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}
for (var i = 0; i < helpText.length; i++) {
  var item = helpText[i];
  document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
}
// 立即执行函数
for (var i = 0; i < helpText.length; i++) {
  (function () {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  })();
}
// let
for (let i = 0; i < helpText.length; i++) {
  const item = helpText[i];
  document.getElementById(item.id).onfocus = () => {
    showHelp(item.help);
  };
}
// forEach
helpText.forEach(function (text) {
  document.getElementById(text.id).onfocus = function () {
    showHelp(text.help);
  };
});

定时器

for (var i = 0; i < 5; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i);
    }, i * 1000);
  })(i);
}

方法通常应该关联于对象的原型

定义到对象的构造器中会形成闭包影响性能

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}