jannahuang / blog

MIT License
0 stars 0 forks source link

闭包 Closure 是什么 #20

Open jannahuang opened 2 years ago

jannahuang commented 2 years ago

闭包

闭包是 JavaScript 的一种语法特性。

闭包 = 函数 + 自由变量 对于一个函数来说,变量分为全局变量、局部变量(函数内部变量)和自由变量。

let count 
function add() { //访问了外部变量的函数
count += 1
}

将上面代码放在非全局环境里,就是闭包。(上例中,闭包是 count + add 组成的整体)。非全局环境可以用立即执行函数或者花括号(局部作用域)实现。

const add2 = function() {
let count
return function add() {
count += 1
}
}()

变量有全局变量和局部变量,由于 JavaScript 特殊的变量作用域,函数内部可以直接读取全局变量,而函数外部无法读取函数内部的局部变量。 要读取函数内部的局部变量,可以在函数内部再定义一个函数,这个新定义的函数可以读取到外层函数的变量。这时把函数 return 或者赋值给全局变量,就可以读取到函数内部的变量。 举例:


function f1(){
var n=999;
function f2(){
console.log(n); // 999
}
return f2;
}

var result = f1(); result(); // 999


闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。可以理解为,能够读取其他函数内部变量的函数。

如果内部函数中引用了外部函数的变量,在这个内部函数被返回并在其他地方被使用后,它仍然引用着那个变量。这是因为内部函数的作用域链包含外部函数的作用域。

在调用一个函数时,会为这个函数调用创建一个执行上下文,并创建一个作用域链。然后用 arguments 和其他命名参数来初始化这个函数的活动对象。外部函数的活动对象是内部函数作用域链上的第二个对象。这个作用域链一直向外串起了所有包含函数的活动对象,直到全局执行上下文才终止。
在函数执行时,要从作用域链中查找变量,以便读、写值。
```javascript
function compare(value1, value2) { 
  if (value1 < value2) {  
    return -1; 
  } else if (value1 > value2) { 
    return 1; 
  } else { 
    return 0; 
  } 
} 

let result = compare(5, 10);

函数执行时,每个执行上下文中都会有一个包含其中变量的对象。全局上下文中的叫变量对象,它会在代码执行期间始终存在。而函数局部上下文中的叫活动对象,只在函数执行期间存在。在定义compare()函数时,就会为它创建作用域链,预装载全局变量对象,并保存在内部的[[ Scope]]中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的[[ Scope]]来创建其作用域链。接着会创建函数的活动对象(用作变量对象)并将其推入作用域链的前端。 作用域链其实是一个包含指针的列表,每个指针分别指向一个变量对象,但物理上并不会包含相应的对象。 作用域链

闭包会保留它们包含函数的作用域,将使用闭包的函数设置为等于 null 会解除对函数的引用,从而让垃圾回收程序可以将内存释放掉。作用域链也会被销毁,除全局作用域之外的其他作用域也可以销毁。

延伸:词法环境

在 JavaScript 中,每个运行的函数,代码块以及整个脚本,都有一个被称为词法环境(Lexical Environment)的内部关联对象。 词法环境对象由两部分组成:

  1. 环境记录:一个存储所有局部变量作为其属性的对象。
  2. 对外部词法环境的引用,与外部代码相关联。

一个“变量”只是环境记录这个特殊内部对象的一个属性。“获取或修改变量”意味着“获取或修改词法环境的一个属性”。

所有的函数在声明时都会记住创建它们的词法环境。所有函数都有名为 [[ Environment]] 的隐藏属性,该属性保存了对创建该函数的词法环境的引用。 [[ Environment]] 有对词法环境的引用。[[ Environment]] 引用在函数创建时被设置并永久保存。 当函数在查找变量时,首先搜索自己的词法环境,然后是外部的词法环境,并且在哪里找到就在哪里修改。 在变量所在的词法环境中更新变量。

闭包的作用

  1. 用的是局部变量,避免污染全局环境。
  2. 可以读取函数内部的变量,提供对局部变量的间接访问。
  3. 让这些变量的值始终保持在内存中,维持变量,使其不被垃圾回收。

以上笔记参考《现代 JavaScript 教程》,《JavaScript 高级程序设计》,JS 中的闭包是什么?学习Javascript闭包(Closure)