JavaSrcript 作用域链,词法环境和闭包

定义

JavaScript 中 内部函数总是可以访问外部函数的变量和参数,即使是在外部函数在return 返回(生命终结)之后

前提:词法环境Lexical Environment

对于代码块{},函数,以及整个js脚本,都有一个对应的词法环境,是一个我们无法通过代码进行访问的对象,这个对象的属性就是我们在其内部定义的变量。这个对象包含两部分:

  1. 自己内部的变量属性
  2. 对外部outer词法环境的引用

所谓作用域链,就是各个词法环境连接成的一个链,我们对链的访问是单向的,只能从内向外,举个例子,嵌套函数中外部函数无法访问内部函数的私有变量,但内部函数可以访问外部函数的变量。原理:每个函数(代码块)有自己的词法环境,其成分是1. 自己的私有变量记录,2. 对外部的引用记录 没有对内部的记录 这就是作用域链单向的原因,下面介绍闭包。

例子:

function makeCounter(params) {
     
    let counter = 0
    return function(params) {
     
        return counter++
    }
}

这里的 makeCounter返回了一个函数,这个函数我们称为嵌套在makeCounter内部。在JavaScript 中嵌套的函数可以访问其外面一层函数的变量(沿着作用域链向外)

我们创建一个 counter

function makeCounter(params) {
     
    let counter = 0
    return function(params) {
     
        return counter++
    }
}

let counter = makeCounter()

重点,counter 在使用makeCounter进行创建时,makeCounter内部的函数作为值return给了counter。counter创建的同时,我们函数词法环境也被创建了,且其内容是根据创建时的情形进行了一次“复制”,即使makeCounter 在return 这个内部函数之后,他的寿命本应该结束了,但生成的词法环境由于有counter对其的引用,垃圾回收器不会将他作为垃圾回收掉。但我们创建了新的词法环境,它的寿命是和counter同样长的,内容是对创建时的拷贝。
我们调用counter,可以看到,counter进行了自增,但这个counter和之前函数声明内的counter不是相同的,是新的词法环境中的

counter();// 0
counter();// 1
counter();// 2

如果我们再声明一个counter2

let counter = makeCounter()
let counter2 = makeCounter()

alert(counter()) // 0
alert(counter2())// 0
alert(counter === counter2)//false

可以看到两个counter是不同的。

回到闭包定义

JavaScript 中 内部函数总是可以访问外部函数的变量和参数,即使是在外部函数在return 返回(生命终结)之后

第一句话的内容在 作用域链 词法环境 被解释
第二句话在 例子 中被解释

关于垃圾回收

垃圾回收也是对词法环境和其内部的变量的回收,如上述提到的makeCounter,如果 函数内部只是对counter进行声明,而明显没有再使用这个变量了,对于V8引擎(chrome ,opera )将会把这个counter 给优化掉,在尝试debug这个变量时返回no such variable.

你可能感兴趣的:(java,script,javascript,es6)