面试官:JavaScript执行机制中的闭包?

前言

JavaScript 中的闭包指的是一个函数以及其捆绑的周边环境状态的引用的组合。闭包可以让开发者从内部函数访问外部函数的作用域,即使外部函数已经执行完毕

今天我们通过JavaScript执行机制来聊聊闭包

正文

首先来分析这段代码的执行机制,这段代码我们主要了解作用域链,以及函数的外部作用域outer

outer是根据词法作用域规则生成的

js 复制代码
function bar() {
    console.log(myName);
}
function foo() {
    var myName = 'Tom'
    bar()
}

var myName = 'Jerry'
foo()

在上述代码中,执行 foo 函数时,在 bar 函数内部打印 myName 时,会查找变量 myName

由于 bar 函数内部没有定义 myName ,它会沿着作用域链向上查找。首先在 foo 函数的作用域中查找,foo 函数中定义了 myName'Tom' ,但是 bar 函数调用时,它无法直接访问 foo 函数作用域内的 myName

接着继续向上查找,在全局作用域中找到了定义的 myName'Jerry' ,所以最终打印的是全局作用域中的 myName ,即 'Jerry'

我们来画一下这个的执行流程

  • 调用栈中创建全局上下文执行对象(变量、词法环境)里面有bar=x001指向堆里面(非原始数据类型存放地)foo myName
  • myName最开始是undefined,全局预编译结束,全局执行,myName=Jerry 调用foo
  • 对foo进行预编译,创建AO对象(foo的执行上下文对象)有变量词法环境,myName=undefined,foo里面代码开始执行,
  • myName=Tom,foo变量环境中还有一个outer指针,值指向全局上下文执行对象,为什么指向全局?
  • outer指向谁取决于foo函数所在的词法作用域在哪里(foo声明在哪里),foo在全局作用域中,所以outer指向全局上下文执行对象
  • 这个指向过程就是作用域链,bar开始调用,创建bar执行上下文,入栈,有变量、词法环境,
  • 变量环境中没有声明东西,但是有outer指针,指向全局上下文对象。
  • 然后开始找myName,自己bar中没有找到,然后顺着outer往全局找,找到了jerry因此打印jerry

这里我们还没具体讲解闭包

接下来我们分析这段代码

js 复制代码
function foo() {
    function bar() {
        var age = 18
        console.log(myName);
    }
    var myName = 'Tom'
    
    return bar
}
var myName = 'jerry'
var fn = foo()
fn()

在上述代码中,执行 fn() 时,在 bar 函数内部打印 myName ,首先在 bar 函数内部查找 myName 变量,未找到。

然后沿着作用域链向上查找,在 foo 函数的作用域中找到了定义的 myName'Tom'

而全局作用域中定义的 myName'jerry' ,但由于作用域链的查找顺序,优先使用了 foo 函数作用域中的 myName ,所以最终打印的是 'Tom'

这我们同样分析一下预编译过程

这里我们就要引出闭包了,当bar执行完了,就得被销户,然后就是foo了,可是这里就出现了一个问题,foo返回了一个方法barbarouter指向的foo,但是foo就被销毁了,可是在全局调用了bar,bar是需要使用到foo里的参数的,于是就还是销毁了foo,但是留下了bar需要的参数,以及他的outer,形成了小书包,这就是闭包

闭包有许多的好处:

  1. 实现数据私有化和封装
    通过闭包,可以将一些变量和函数隐藏在内部函数中,外部无法直接访问和修改,从而实现了一定程度的封装和数据保护。
  2. 保持函数执行的上下文和状态
    闭包能够让函数记住其创建时的环境状态,即使外部函数已经执行完毕,内部函数仍然可以访问和操作这些状态信息。这对于实现需要保持状态的功能非常有用,比如计数器、缓存等。
  3. 模拟私有方法
    在 JavaScript 中没有真正的私有方法,但可以利用闭包来模拟私有方法,只在特定的函数内部可访问和操作。
  4. 函数柯里化和偏函数应用
    闭包有助于实现函数柯里化和偏函数应用,使函数的参数传递更加灵活和可定制。
  5. 模块模式
    可以使用闭包创建模块,模块中的变量和方法只在模块内部可访问,对外只暴露必要的接口。

那么让我们分析一下这段代码

js 复制代码
function add() {
    let count = 0;
    function fn() {
        count++
        return count;
    }
    return fn;
}
var res = add();
console.log(res());
console.log(res());
console.log(res());

add函数的调用是不依赖全局变量的,封装函数 实现累加

add 函数返回了内部函数 fn

当执行 var res = add(); 时,res 接收到了 fn 函数,并且 fn 函数形成了一个闭包,能够访问到 add 函数中的 count 变量。

第一次执行 console.log(res()); 时,count 自增为 1 并返回,所以打印 1

第二次执行 console.log(res()); 时,count 再次自增为 2 并返回,所以打印 2

第三次执行 console.log(res()); 时,count 继续自增为 3 并返回,所以打印 3

总结

本文讲解了JavaScript的执行机制和JavaScript执行机制中的闭包,相信看到这里的你一定会有收获的!!!!

相关推荐
初学者↑8 分钟前
Java代码生成器(开源版本)
java·开发语言
loveLifeLoveCoding10 分钟前
Java 内存分页
java·开发语言
2.5条悟T^T11 分钟前
String类
java·c语言·开发语言·jvm·数据结构·算法·servlet
mayo的自留地19 分钟前
window10/11如何下载安装JDK(jdk) 超详细图文教程
java·开发语言·jvm·spring·servlet·tomcat·nio
立秋678921 分钟前
使用Python绘制极坐标图
开发语言·python
kaixin_learn_qt_ing22 分钟前
QListView自定义item(结合QSqlQueryModel)
开发语言·qt
信息科技云课堂33 分钟前
在 Python 中将字典内容保存到 Excel 文件
开发语言·python·excel
文 丰34 分钟前
python基础_类
开发语言·python
用哲学编程37 分钟前
每日一题——Python实现PAT乙级1090 危险品装箱(举一反三+思想解读+逐步优化)4千字好文
开发语言·python·算法·职场和发展
CV工程师小林1 小时前
【C++】C++11
开发语言·c++