JavaScript 闭包

闭包

关于 JavaScript 的初学者来讲,闭包的观点和运用都能够算的上是难点。 MDN 的 JavaScript 文档对闭包的观点给出了正确的定义,也供应了简朴直观的的实例,是一个非常好的学习材料。 这篇文章将从文档动身,对闭包的知识点举行一个简朴的梳理。

闭包是什么

起首,我们需要对闭包供应一个正确的定义。 在文档中,闭包的定义是 ‘A closure is the combination of a function and the lexical environment within which that function was declared’。这个定义是很拗口的一句话。 词法环境(lexical environment)这个形貌关于初学者来讲过于学术和笼统,我们只需要记着就好。真正明白定义最好体式格局就是经由过程现实的代码。 假定:

function init() {
   var name = "Hello"; // name 是一个被 init 建立的局部变量
   function displayName() { 
       // displayName() 是内部函数,一个闭包
       alert(name); // 使用了父函数中声明的变量
   }
   displayName(); 
}
init();

经由过程以上的代码块来看,我们能够看到闭包现实上指的就是一个’具有外部环境变量的函数‘。 在上面的例子中函数 displayName 挪用了不属于自身的外部变量 name,不论此 displayName 函数终究是不是被返回,现实上由 name 和 displayName 构成的闭包已构成。

 function init() {
    var name = "Hello"; // name 是一个被 init 建立的局部变量
    function displayName() { 
        // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    return displayName(); // 闭包被返回
}
var fun = init();
fun();

我们再来看一下这一块的新的代码,唯一的区分在于这个代码中 函数init 返回了一个函数 displayName()。也就是返回了一个闭包。经由过程这个返回的闭包,我们就可以够接见这个函数所相关联的词法环境或者说数据。原本应该被烧毁的 name 变量保存了下来,而且只能经由过程挪用闭包的体式格局来接见,这也就是私有性。

闭包的作用

现实上在上一个例子中,我们已看到了闭包的作用,闭包能够用来模仿私有变量和要领。 它让函数和函数所操纵的某些数据(环境)关联了起来。

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

在上面的例子中 我们能够看到变量 privateCounter 和 函数 changeBy 作为下面三个函数配合的词法环境构成了闭包。 在 makeCounter()实行以后, 本该消逝的词法环境被保存下来,只能经由过程返回的三个函数举行变动和接见。这类行动模仿出了相似 JAVA 类中的私有变量和私有要领。

在轮回中建立闭包:一个罕见毛病;

在 ECMAScript 2015 引入 let 这个关键字之前,在轮回中有一个罕见的闭包建立题目。

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
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();

这段代码的效果就是,不管你挑选哪个输入框,helper 信息永久都邑显现 Your age (you must be over 16)’。 缘由就在于在返回的三个闭包现实上同享了 item 这一个词法环境,所以 helper 永久只显现为末了 age 的 helper。 这里就是闭包里另一个很主要的知识点,闭包只会捕捉自在变量的援用,所以当 item 指向的helpText值末了变成 age 时,三个闭包的中的 item 也都变成了 age。 依据这一点我们能够将代码修正以下

function setupHelpAnonymous(){
    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];
      (function() {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = function() {
          showHelp(item.help);
        }
     })();
    }
  }

  setupHelpAnonymous()

在上面的代码片断中 我们使用了一个 IIFE (马上实行函数表达式) 对 item 这个援用举行了马上求值。如许我们就可以获得想要的效果。而在ES6中的 ’块级作用域‘ 也能够处理这个题目。

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++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

每一次轮回,都有一个新的 item 被建立,三个闭包不再同享同一个词法环境;比拟匿名闭包的体式格局,也没有建立过剩的闭包。

    原文作者:promer94
    原文地址: https://segmentfault.com/a/1190000013430283
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞