闭包

const secureBooking = function () {//-----------------1
  let passengerCount = 0;//-------------------------1.1
  return function () {//----------------------------1.2
    passengerCount++;//---------------------------1.2.1
    console.log(`${passengerCount} passengers`);//1.2.2
  };
};
const booker = secureBooking();//----------------------2
booker();//--------------------------------------------3
booker();//--------------------------------------------4

首先看上述函数,是一个简单的闭包案例。对以上代码进行执行上下文创建的流程分解(不清楚执行上下文的概念可参考执行上下文):

  1. 创建全局上下文global()推入调用栈并声明函数secureBooking();

  2. 运行2调用secureBooking函数创建该函数的执行上下文推入调用栈;

  3. 运行1.1在secureBooking执行上下文中声明passengerCount变量环境;

  4. 运行return弹出secureBooking执行上下文并转回步骤3;

  5. 运行3调用booker创建booker的执行上下文推入栈,而1.2下面没有声明任何变量环境,此时仍能访问被弹出secureBooking执行上下文的passengerCount(闭包功能),并执行passengerCount++,最后执行完弹出栈;

  6. 与5类似调用booker后创建新的执行上下文并在闭包中找到变量执行++(passengerCount此时为1了,所以最后输出2),最后弹出栈;

因此从中我们得出闭包相关结论:

????闭包是指被返回调用的子函数可以访问在父函数中创建的执行上下文的变量环境,即使该执行上下文被弹出(即执行上下文对应的函数运行到return)也可以访问。该子函数关闭了父函数的作用域链,可以一直引用父作用域链,其中包括所有变量环境。

????闭包确保一个函数永远不会失去该父级函数中定义的变量联系。

????另外每当函数查找变量先查找闭包是否存在该变量,再在其作用域中查找,即闭包查找优先于作用域链,即使是全局作用域也闭包优先。

闭包无法显示访问,关闭或打开闭包获取变量,它只是一个内部属性,我们可以通过以下方法查看:

console.dir(booker);