什么是闭包?(写给前端小白的理解条记)

打印 上一主题 下一主题

主题 1061|帖子 1061|积分 3183

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
闭包指的是那些 引用了另一个函数作用域中变量的函数 通常是在嵌套函数中实现的.
10.14.1. 闭包函数的作用域链

  1. function createComparisonFunction(propertyName) {
  2.   return function(object1, object2) {
  3.     let value1 = object1[propertyName];
  4.     let value2 = object2[propertyName];
  5.     if (value1 < value2) {
  6.       return -1;
  7.     } else if (value1 > value2) {
  8.       return 1;
  9.     } else {
  10.       return 0;
  11.     }
  12. };
  13. }
复制代码
上面的代码3,4行 在返回的匿名函数中 引用了外部函数的变量propertyName .。在这个内部函数被返回并在其他地方被使用后,它仍旧引用着那个变量。这是由于内部函数的作用域链包罗 createComparisonFunction()函数的作用域。
每一个上下文都有一个关联的一对一的变量对象. 上下文中的代码在执行的时间,会创建变量对象的一个作用域链. 这个作用域链决定了各级上下文中的代码在访问变量和函数时的序次.


  • 代码正在执行的上下文的变量对象始终位于作用域链的最前端.


  • 作用域链中的下一个变量对象来自包罗上下文,再下一个对象来自再下一个包罗上下文,以此类推直至全局上下文.
  • 全局上下文的变量对象 始终是作用域链的最后一个变量对象.

  • 普通函数 作用域链创建和使用的细节
  1. function compare(value1, value2) {
  2. if (value1 < value2) {
  3. return -1;
  4. } else if (value1 > value2) {
  5. return 1;
  6. } else {
  7. return 0;
  8. }
  9. }
  10. let result = compare(5, 10);
复制代码
这里界说的 compare()函数是在全局上下文中调用的。当调用 compare()函数时,会为它创建一个包罗参数 arguments、value1 和 value2 局部变量、和this指向等内容的活动对象,这个对象是其作用域链上的第一个对象。
而全局上下文的变量对象则是 compare()作用域链上的第二个对象,此中包罗 compare、 result 和 this 等内容。
界说 compare()函数时,就会为它创建作用域链,作用域链中预装载全局变量对象,并保存在内部的[[Scope]]中。
调用这个函数时,会创建相应的执行上下文,然后通过复制函数的[[Scope]]来创建其作用域链。接着会创建函数的活动对象(用作变量对象)并将其推入作用域链的前端。在这个例子中,这意味着 compare() 函数执行上下文的作用域链中有两个变量对象:局部变量对象和全局变量对象。
作用域链着实是一个包罗指针的列表,每个指针分别指向一个变量对象,但物理上并不会包罗相应的对象,只是存储对象的引用所在 指向该对象。


compare函数内部 在访问变量时 就会根据给定的名称从作用域链中查找变量。 函数执行完毕之后 该函数对应的局部活动对象会被销毁,内存中只剩下全局作用域。
不过,闭包就不一样了。

  • 闭包函数 作用域链创建和使用的细节
  1. function createComparisonFunction(propertyName) {
  2.   return function(object1, object2) {
  3.     let value1 = object1[propertyName];
  4.     let value2 = object2[propertyName];
  5.     if (value1 < value2) {
  6.       return -1;
  7.     } else if (value1 > value2) {
  8.       return 1;
  9.     } else {
  10.       return 0;
  11.     }
  12. };
  13. }
复制代码
在一个函数内部界说的函数 会把其包罗函数的活动对象添加到本身的作用域链中。
因此,在 createComparisonFunction()函数中,匿名函数的作用域链中现实上包罗 createComparisonFunction()的活动对象。 如下图所示


  1. let compare = createComparisonFunction('name');
  2. let result = compare({ name: 'Nicholas' }, { name: 'Matt' });
复制代码
在 createComparisonFunction()返回匿名函数后(即第一行代码执行后),它的作用域链被初始化为包罗 createComparisonFunction()的活动对象和全局变量对象。这样,匿名函数就可以访问到 createComparisonFunction()可以访问的所有变量。另一个故意思的副作用就是createComparisonFunction()的活动对象并不能在它执行完毕后销毁,由于匿名函数的作用域链中仍旧有对它的引用。在 createComparisonFunction()执行完毕后,其执行上下文的作用域链会销毁,但它的活动对象仍旧会保存在内存中,直到匿名函数被销毁后才会被销毁. 如下图所示


  1. // 创建比较函数
  2. let compareNames = createComparisonFunction('name');
  3. // 调用函数
  4. let result = compareNames({ name: 'Nicholas' }, { name: 'Matt' });
  5. // 解除对函数的引用,这样就可以释放内存了
  6. compareNames = null;
复制代码
这里,创建的比较函数(即createComparisonFunction函数返回的匿名函数)被保存在变量 compareNames 中。把 compareNames 设置为等于 null 会解除对该匿名函数的引用,从而让垃圾接纳程序可以将内存开释掉。作用域链也会被销毁,其他作用域(除全局作用域之外)也可以销毁。


注意 由于闭包会保存它们包罗函数的作用域,所以比其他函数更占用内存。过度使用闭包大概导致内存过度占用,因此建议仅在十分须要时使用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表