JavaScript的环境模型

环境模型这个概念它用于解释Scheme的函数计算规则

环境是什么

环境在计算过程中必不可少, 因为他决定了计算表达式的上下文。 可以这样认为, 表达式本省在语言里毫无意义, 表达式的意义取决于他计算时所在的环境。

JavaScript的解释器就充当着环境的角色。 在该环境下, 表达式1+1 的计算结果为2, 表达式Date()调用一个函数并返回当前的时间, 表达式 () => 1 定义一个返回1的函数, 总之, 对程序而言, 环境就是在计算过程为符号提供实际意义的东西。

环境模型

环境模型中的环境具体指的是变量环境。 函数在计算时根据环境决定变量的值, 从而决定它的计算结果。

 

环境的创建和作用

函数在调用时会先创建一个环境, 然后在该环境中计算数的内容

function add10 (value) {
    var increament = 10;
    return value + increament;
}

表达式add10(2)的计算过程:

  • 创建环境
  • 给环境add中的变量赋值为2
  • 进入环境
  • 在环境add10中给变量increment赋值为10
  • 在环境add10中获得变量value的值为2
  • 在环境add10中获得变量increment的值10
  • 计算表达式2 + 10
  • 返回12
  • 离开环境add10

值得一提的是 形参也是变量, 他在形参列表里定义在函数调用时获得初始值。

 

变量绑定

环境使用变量绑定来存放变量的值, 绑定与函数中的变量一一对应

约束变量和自由变量

在函数中 定义一个变量, 变量的意义取决于函数的内容, 它的作用范围也被约束在函数之中, 此时的变量被称为约束变量。

在函数中使用一个没有定义的变量 它的作用范围不收函数的约束, 此时的变量称为自由变量。

function main() {   //1
    var x = 10; //2
    var addX = function (value) {   //3
        var increment = x;  //4
        return value + increment;   //5
    };  //6

    var value = 2;  //7
    addX(value);    //8
}   //9
main(); //10
  • 在函数main中,变量x(>2、4),addX(>3、8),value(>7、8)皆为约束变量。

  • 在函数addX中,变量value(>3、5),increment(>4、5)是约束变量,变量x(>4)是自由变量。

绑定与变量

在函数的计算过程中,变量定义会使当前的环境加入对应的绑定。

上文中表达式main()(>10)的计算过程产生了2个环境,$main和$addX:

  • 环境$main拥有3个绑定,x,addX,*value。

  • 环境$addX拥有2个绑定,value,increment。

可见,绑定存放的是约束变量的值,约束变量的值可以直接从当前环境获取。

 

而自由变量的值需要从其他环境获取,该环境是自由变量定义时所在的环境,拥有自由变量的绑定。

上文中表达式addX(value)(>8)的计算过程:

  • 获得环境$main中绑定*addX的值addX函数。(>8)

  • 获得环境$main中绑定*value的值2。(>8)

  • 修改环境$addX中绑定*value的值为2。(>8)

  • 获得环境$main中绑定*x的的值10。(>4)

  • 修改环境$addX中绑定*increment的值为10。(>4)

  • 获得环境$addX中绑定*value的值2。(>5)

  • 获得环境$addX中绑定*increment的值10。(>5)

计算function表达式或lambda表达式会得到一个函数,这种情况一般被称为函数定义。方便起见,本文将值是变量的函数称为函数。

就这样,函数在计算时只要找到对应的绑定,就能确定一个变量的值。

 

环境的引用

环境不仅保存了变量绑定, 还会保存一个环境引用 enviroment pointer 环境引用指向其他的变量环境。 通过环境引用, 自由变量可以从其他环境寻找自己对应的绑定

环境引用的来源

函数在定义时会把当前环境的引用记录下来。在调用函数后, 新的环境会得到函数中的环境引用并将此保存。

也就是说一个函数在计算时的环境,拥有函数在定义时环境的引用

var getCounter = function (start) { //1
    return function () {    //2
        return start++; //3
    };  //4
};  //5
var counter = getCounter(0);    //6
counter();  //7

 

表达式getCounter(0)(>6)和counter()(>7)分别创建了两个环境:

  • 环境$getCounter拥有全局环境的引用。

  • 环境$counter拥有环境$getCounter的引用。

一些看似不在函数中定义的函数,其定义时也身处环境中,该环境被称为全局环境。函数getCounter就保存了全局环境的引用。

环境引用与绑定

函数在计算过程中定义函数, 如同代码文本结构那样一层包裹一层,里层的函数定义是外层函数中的一条表达式,里层函数创建的环境通过引用连接外城函数创建的环境。

因此,一个变量在当前环境找不到对应绑定值的时,可以通过引用一层层回溯到它定义时所在的环境, 从而找到该绑定。 自由变量便是通过这种方式找到自己对应的绑定

上文中表达式counter()(>7)的计算过程:

  • 使用变量counter。(>7)

  • 在当前环境(全局环境)找到变量绑定*counter,它的值是一个函数 。

  • 调用函数counter会创建环境$counter。(>7)

  • 环境$counter从函数counter得到环境$getCounter的引用。

  • 进入环境$counter。

  • 使用变量start。(>3)

  • 在环境$counter找不到绑定*start。

  • 环境$counter通过引用定位到环境$getCounter。

  • 在环境$getCounter中找到绑定*start。

  • 返回绑定*start的值0作为函数的计算结果。(>3)

  • 令绑定*start的值自增1,从0变为1。(>3)

  • 离开环境$counter。

每次计算表达式counter(),绑定*start的值都会自增1,并依次返回0,1,2,3……

 

总结

  • 函数在定义时会保存当前环境的引用
  • 一旦函数被调用, 就会创建一个新的环境, 新的环境拥有函数定义时环境的引用
  • 函数中的变量定义表达式会新环境加入绑定
  • 函数使用变量就是访问环境中对应的绑定
  • 如果变量在当前环境找不到对应的绑定, 就会通过引用一层层回溯到他定义时所在的环境 从而找到他的绑定
  • 而这种访问其他变量环境的机制, 通常被人称为闭包

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值