`

深入--闭包

 
阅读更多

定义

 

MDN对闭包的定义为: 闭包是指那些能够访问自由变量的函数

那什么是自有变量呢?自有变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量

由此,我们可以看出闭包共有两部分组成:闭包 = 函数 + 函数能够访问的自由变量

 

举个例子

var a = 1;
function foo(){
     console.log(a);
}
foo();

 foo函数可以访问变量a,a对foo来说是自由变量,那么foo就是闭包了。。。

 

所以在《JavaScript权威指南》中就讲到:从技术的角度讲,所有的JavaScript函数都是闭包。

这怎么跟我们平时认识的闭包不一样呢?

别急,这是理论上的闭包,还有一个时间角度上的闭包。

ECMAScript中,闭包指的是:

1.从理论角度:所有函数,因为他们在创建的时候就将上层上下文的数据保存起来了。

2.从实践角度:一下函数才算闭包:

   ①即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)

   ②在代码中引用了自由变量

接下来讲讲实践上的闭包

 

分析

 

让我们写个例子,例子依然是来自《JavaScript权威指南》,稍微做点改动:

var scope = "global scope";
function checkscope(){
      var scope = "local scope";
      function f(){
           return scope;
      }
      return f;
}
var foo = checkscope();
foo();

 首先我们分析一下这段代码中执行上下文栈和执行上下文的变化情况

 

另一个与这段代码相似的例子,在《深入--执行上下文》中有非常详细的分析

这里直接给出简要的执行过程:

1.进入全局代码,创建全局执行上下文,并压栈

2.全局执行上下文初始化

3.执行checkscope函数,创建chenckscope函数执行上下文,并压栈

4.checkscope函数执行上下文初始化,创建变量对象、作用域链、this等

5.checkscope函数执行完毕,checkscope执行上下文从执行上下文栈中弹出

6.执行f函数,创建f函数执行上下文,并压栈

7.f函数执行上下文初始化,创建变量对象、作用域链、this等

8.f函数执行完毕,f函数执行上下文从栈中弹出

 

了解到这个过程,我们应该思考一个问题,那就是:

当f函数执行的时候,checkscope函数上下文已经被销毁了啊(即从栈中弹出),怎么还会读取到checkscope作用域下的scope值呢?

 

当我们了解了具体的执行过程后,我们知道f执行上下文维护了一个作用域链:

fContext = {
    Scope:[AO,checkscopeContext.AO,globalContext.VO],
}

 就是因为这个作用域链,f函数依然可以读取到checkscopeContext.AO的值,说明当f函数引用了checkscopeContext.AO中的值时,即使checkscopeContext被销毁了,但是js依然会让checkscopeContext.AO活在内存中,f函数依然可以通过作用域链找到他,正是js做到这一点,从而实现了闭包这个概念。

 

所以,让我们再看一遍实践角度上的闭包的定义:

1.即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)

2.在代码中引用了自有变量

 

必刷题

 

接下来,看这道刷题必刷,面试必考的闭包题

var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]();

 答案都是3,让我们分析一下原因

 

当执行到data[0]函数之前,此时全局上下文的VO为:

globalContext = {
    VO:{
          data:[...],
          i:3
    }
}

 当执行到data[0]函数的时候,data[0]函数的作用域链为:

data[0]Context = {
      Scope:[AO,globalContext.VO]
}

 dat[0]Context的AO并没有i值,所以会从globalContext.VO中查找,i为3,所以打印的结果就是3.data[1]和data[2]是一样的道理

 

所以让我们改成闭包看看:

var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = (function (i) {
        return function(){
            console.log(i);
        }
  })(i);
}

data[0]();
data[1]();
data[2]();

 当执行到data[0]函数之前,此时全局上下文的VO为:

globalContext = {
    VO: {
        data: [...],
        i: 3
    }
}

 跟没改之前一模一样。

当执行data[0]函数的时候data[0]函数的作用域链发生了改变:

data[0]Context = {
     Scope : [AO,匿名函数Context.AO,globalContext.VO]
}

 匿名函数执行上下文的AO为:

匿名函数Context = {
          AO : {
                 arguments :{
                      0:0,
                      length:1
                  },
                  i:0
          }
}

 data[0]Context的AO并没有i值,所以会沿着作用域链从匿名函数Context.AO中查找,这时候就会找i为0,找到了就不会往globalContext.VO中查找了,即使globalContext.VO也有i的值(值为3),所以打印的记过就是0。

data[1]和data[2]是一样的道理

分享到:
评论

相关推荐

    离散数学-闭包运算-.net

    离散数学是计算机科学的基础,尤其在理论计算机科学、数据结构和算法设计中扮演着重要角色。闭包运算则是离散数学中的一个...理解并掌握这些知识点,对于深入学习计算机科学,尤其是算法设计和复杂系统分析至关重要。

    ios-闭包传值.zip

    本教程将深入探讨如何在iOS应用中使用闭包进行值传递。 闭包的基本语法: 闭包的语法通常由花括号 `{}` 包围,其中包含一组语句。闭包可以是匿名的,即没有名称,也可以通过类型别名或函数指针进行命名。在Swift中...

    swift菜鸟入门视频教程-07-闭包

    在这个“Swift菜鸟入门视频教程-07-闭包”中,我们将深入探讨Swift中的一个重要概念——闭包。闭包在编程中是一种功能强大且灵活的工具,它允许我们定义可存储和传递的匿名函数。 闭包在Swift中的基本概念: 1. **...

    第九课 闭包-011

    本节我们将深入探讨匿名函数和闭包,并了解它们在实际应用中的作用。 首先,我们来看匿名函数。匿名函数,正如其名,是没有名称的函数。它们可以直接在代码中定义并立即执行,或者与其他语法结构结合使用。例如,...

    groovy(10)-闭包委托策略1

    让我们深入理解这些概念: - 当我们在`scriptClouser`中打印`this`, `owner`, 和 `delegate`时,它们的值都是`variable.Clousershujujiegou@17046283`,因为它们都在同一个作用域内。 - 在`Preson`类的静态内部类`...

    L-闭包空间的rs-连通性 (2010年)

    通过引入r-闭包这一新概念,作者对该连通性进行了深入分析。 #### 引言与预备知识 首先,文中提到连通性是拓扑学的一个核心研究内容。近年来,许多国内外学者在该领域取得了显著成就。特别地,本文聚焦于L-闭包...

    论文研究-闭包算子与邻域系的广义化.pdf

    在一般拓扑学中,拓扑结构可以通过一系列基本概念来刻画,如邻域、内部、闭包以及...通过这种广义化的方法,研究者们可以更深入地探索各种复杂结构的本质,包括但不限于点集合拓扑学、序结构、以及它们之间的相互关系。

    关于S-集与S-闭包 (1987年)

    本文探讨了拓扑空间中的S-集与S-闭包的概念,并在此基础上研究了S-闭空间的...通过对S-集和S-闭包的研究,数学家能够更深入地理解拓扑空间的性质,这对于整个数学领域,特别是拓扑学和相关数学分支的发展具有重要意义。

    深入理解javascript原型和闭包

    深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系

    C语言实现三种闭包算法(传递,自反,对称闭包)

    总结一下,实现C语言的闭包算法需要对数据结构和算法有深入理解,包括数组、链表的使用,以及迭代和递归的思想。虽然C语言的特性使得实现起来相对复杂,但通过合理的设计,我们可以有效地模拟这些高级编程概念。

    关系闭包的计算

    本实验旨在通过编程实践的方式帮助学习者深入理解关系闭包的概念,并熟练掌握Warshall算法用于计算关系的自反闭包、对称闭包以及传递闭包。 #### 关键概念解释 1. **自反闭包**:给定集合A上的关系R,如果对于集合...

    图论- 图的连通性- 传递闭包.rar

    总的来说,图的连通性和传递闭包是图论中深入研究的课题,它们不仅有助于理解图的基本性质,还为解决实际问题提供了理论基础。通过对这些概念的深入学习和掌握,我们可以更好地解决网络中的路径查找、关系分析等问题...

    ios-switf 闭包与代理.zip

    首先,我们来深入理解闭包。闭包是Swift中的一个强大特性,它可以捕获和存储其所在上下文中的常量和变量。简单来说,闭包就是一个可以携带环境的匿名函数。它可以在代码中被当作值传递,也可以存储在变量中,甚至...

    Swift之闭包ClosureDemo

    通过这个Demo,你可以深入理解闭包如何与Swift的其他特性和设计模式相结合,提升代码的可读性和效率。 总结来说,Swift的闭包是实现函数式编程风格、简化异步操作以及提高代码复用性的重要工具。通过实践和学习...

    js代码-闭包-携带状态的函数

    让我们深入探讨一下闭包的概念、工作原理以及如何在实际的JavaScript代码(如`main.js`)中应用它们。 闭包的本质在于,它是一个函数,能够记住并访问创建它的词法作用域,即使这个作用域已经不再存在。在...

    JavaScript闭包深入理解.pdf

    深入理解闭包,还需要了解JavaScript的作用域、作用域链、执行上下文等概念。作用域决定了变量的可见性,全局作用域在整个脚本中都可访问,而函数作用域仅在函数内部可见。当函数执行时,会创建一个执行上下文,其中...

    深入解析Javascript闭包的功能及实现方法

    JavaScript中的闭包是一种强大的特性,它允许函数访问和操作其外部作用域的变量,即使在其外部函数已经执行完毕后。闭包的实现基于JavaScript的作用域链和垃圾回收机制。 1. **闭包的概念** 闭包是一个拥有自身...

    用矩阵求自反闭包自反闭包

    因此,深入理解自反闭包的概念和矩阵表示法对于学习这些领域的知识是十分必要的。 综上所述,用矩阵求自反闭包是一种有效且直观的方法,它将抽象的关系理论与具体的数学运算相结合,帮助我们更好地理解和处理关系...

Global site tag (gtag.js) - Google Analytics