深度JavaScript章节:环境和闭包如何在JavaScript中工作?

2020-07-26 16:17:26

在本章中,我们将进一步了解ECMAScript语言规范是如何处理变量的。

环境是ECMAScript规范用来管理变量的数据结构。它是一个字典,其键是变量名,值是这些变量的值。每个作用域都有其关联的环境。环境必须能够支持以下与变量相关的现象:

函数f(X){return x*2;}函数g(Y){const tmp=y+1;return f(Tmp);}断言。相等(g(3),8);

对于每个函数调用,需要为被调用函数的变量(参数和局部变量)提供新的存储空间。这是通过所谓的执行上下文堆栈来管理的,执行上下文是对环境的引用(就本章而言)。环境本身存储在堆中。这是必要的,因为它们偶尔会在执行离开其作用域之后继续存在(我们将在探索闭包时看到这一点)。因此,它们本身不能通过堆栈进行管理。

函数f(X){//暂停3返回x*2;}函数g(Y){const tmp=y+1;//暂停2返回f(Tmp);}//暂停1断言。相等(g(3),8);

其余步骤:每次返回时,从堆栈中删除一个执行上下文。

函数f(X){函数Square(){const result=x*x;return result;}return square();}断言。相等(f(6),36);

在这里,我们有三个嵌套的作用域:顶级作用域、f()的作用域和square()的作用域。观察:

望远镜已经连接好了。内部作用域“继承”外部作用域的所有变量(减去它所隐藏的变量)。

嵌套作用域作为一种机制独立于递归。后者最好由一组独立的环境来管理。前者是每一个环境与它被创造的“其中”的环境之间的一种关系。

因此,每个作用域的环境通过一个名为out的字段指向周围作用域的环境。当我们查找变量的值时,我们首先在当前环境中搜索它的名称,然后在外部环境中搜索,然后在外部环境的外部环境中搜索,依此类推。整个外部环境链包含当前可以访问的所有变量(减去隐藏的变量)。

当您进行函数调用时,您将创建一个新环境。该环境的外部环境是创建函数的环境。为了帮助设置通过函数调用创建的环境外部的字段,每个函数都有一个名为[[Scope]]的内部属性,该属性指向其“出生环境”。

函数f(X){函数Square(){const result=x*x;//暂停3返回结果;}//暂停2返回正方形();}//暂停1断言。相等(f(6),36);

要了解如何使用环境实现闭包,我们使用以下示例:

函数add(X){return(Y)=>;{//(A)return x+y;};}断言。等于(add(3)(1),4);//(B)。

这里发生什么事情?Add()是一个返回函数的函数。当我们在B行进行嵌套函数调用add(3)(1)时,第一个参数用于add(),第二个参数用于它返回的函数。这之所以有效,是因为在A行中创建的函数在离开其出生范围时不会丢失与该范围的连接。关联的环境通过该连接保持活动状态,并且函数仍然可以访问该环境中的变量x(x在函数中是空闲的)。

这种嵌套调用add()的方式有一个优点:如果只调用第一个函数,则会得到一个Add()版本,其参数x已经填充:

将一个有两个参数的函数转换为两个嵌套函数,每个函数有一个参数,这称为Currying。Add()是一个临时函数。

只填写函数的部分参数称为部分应用(该函数尚未完全应用)。函数的方法.bind()执行部分应用程序。在前面的示例中,我们可以看到,如果函数是curry,则部分应用程序是简单的。

函数add(X){return(Y)=>;{//暂停3:plus2(5)return x+y;};//暂停1:add(2)}常量plus2=add(2);//暂停2断言。相等(加2(5),7);