重新理解js的执行环境和闭包

Posted 造轮子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重新理解js的执行环境和闭包相关的知识,希望对你有一定的参考价值。

数学是美的,是一种精确的美。开发语言作为数学的一种映射,我相信也一定是美的,精确美。
最近突然发现对一些基本概念模棱两可甚至莫名其妙。我相信开发是具备精确性的所以我决定重新理解,确定这些概念的边界和内涵。
 
关于执行环境
执行环境是javascript中最为重要的一个概念。执行环境定义了变量或者函数有权访问其他数据,决定了他们各自的行为。
每个执行环境都有一个与之关联的变量对象。环境中定义的所有变量和函数都保存在这个对象中。全局执行环境是最外层的执行环境。
在JavaScript中每个函数都有自己的执行环境。当代码在一个执行环境中执行时,会创建变量对象的作用域链。作用域链保证对执行环境有权访问的所有变量和函数的有序访问。
作用域链的前端是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开水时只包含一个变量,即arguments对象(箭头函数没有这个)。作用域链中的下一个变量对象来自外部环境,而下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境。
 
关于作用域
作用域是一个范围。它的主体可以是变量或者函数。它可以分为全局作用域、局部作用域、块级作用域。
 
关于闭包
在《JavaScript高级程序设计》中,对闭包的定义是有权访问另一个函数作用域的变量的函数。按照这个定义闭包是一个函数。而且这个函数需要被另一个函数包裹。
 
而且有点比较重要:
一般来说函数执行完毕后,局部活动UI对象就会被销毁,内存中仅保存全局作用域。但是闭包不同,因为闭包的内部函数会引用外部函数变量,导致活动对象不会被销毁。而闭包的这种配置机制也就引起了一个副作用,即闭包只能取得包含函数中任何变量的最后一个值。
比如:
 
 1 function createFunctions() {
 2             const arr = []
 3             for (var i = 0; i < 10; i++) {
 4                 arr.push(function () {
 5                     console.log(i)
 6                 })
 7             }
 8             return arr
 9             
10         }
11 
12         createFunctions().forEach((fn)=>{
13           fn()
14         })

但是还有这样的一种情形 

1         // const arr = []
2         for (var i = 0; i < 10; i++) {
3             arr.push(function () {
4                 console.log(i)
5             })
6         }
7         arr.forEach(function (fn) {
8             fn()  // 输出的是10
9         })

上述两者的打印结果都是10。但原理不是一样的。

 第一个是闭包行为,第二个是全局全局作用域一直存在,而执行完毕全局作用域的i是10。
当然要想解决上述问题只需要把var改为let即可。因为let具备块级作用域。因为存在块级作用域,循环中i只会在for循环的环境中,一旦脱离就不在存在,所以访问不到了,所以按部就班的输出。

以上是关于重新理解js的执行环境和闭包的主要内容,如果未能解决你的问题,请参考以下文章

理解运用JS的闭包高阶函数柯里化

深入理解javascript原型和闭包(11)——执行上下文栈

JavaScript之闭包(重新认识)

JS闭包的概念

理解JS闭包——以计数器为例

JS核心知识点梳理——闭包