首页IT科技作用域链是什么(夯实基础中篇-图解作用域链和闭包)

作用域链是什么(夯实基础中篇-图解作用域链和闭包)

时间2025-07-30 20:46:03分类IT科技浏览4625
导读:前言 本文承接上篇 夯实基础上篇-图解 JavaScript 执行机制,请...

前言

本文承接上篇 夯实基础上篇-图解 JavaScript 执行机制              ,请先阅读上篇~

讲基础不容易                  ,本文通过 7个demo和6张图      ,和大家一起学习温故作用域链和闭包           ,本文大纲:

什么是作用域链 什么是词法作用域 什么是闭包 闭包的实际使用案例

什么是作用域链

正文开始~

请思考下面 demo 的 name 打印什么

function test() { console.log(name) } function test1() { const name = test1的name test() } const name = global的name test1()

通过执行上下文来分析代码的执行流程                  ,执行到 test 函数时:

那 test 函数里的 name 是哪个呢?这就涉及到了作用域链的定义:变量和函数的查找链条就是作用域链              。它决定了各级上下文中的代码在访问变量和函数时的顺序:查找变量和函数时         ,先在当前执行上下文找        ,当前没有                   ,到下一个执行上下文找            ,没有再到下一个    ,直到全局执行上下文                    ,都没有就报错使用未定义的变量或函数                  。

而在每个执行上下文的变量环境中               ,都包含了一个外部引用 outer,用来指向外部的执行上下文                 ,链条结构是当前执行上下文 > 包含当前上下文的上下文1 > 包含上下文1的上下文2 ...      。

而这个 demo 会打印global的name                  ,原因是 test 执行上下文的 outer 指向全局执行上下文   ,包括 test1 的 outer 也是指向全局执行上下文:

也许会有同学疑惑              ,为什么 test 的 outer 指向全局执行上下文                  ,而不是 test1      ,这是因为在 JavaScript 执行过程中           ,其作用域链是由词法作用域决定的           。

什么是词法作用域

词法作用域就是作用域是由代码中函数声明的位置来决定的                  ,它是静态的作用域         ,通过它就能够预测代码在执行过程中如何查找标识符        ,它与函数是怎样调用的没有关系                  。所以刚才的例子打印的是global的name         。

看个具体例子:

const count = 0 function test() { const count = 1 function test1() { const count = 2 function test2() { const count = 3 } } }

其包含关系和作用域链:

事实上在 Global Scope 全局作用域(Window)之前                   ,还有一个 Script Scope 脚本作用域            ,它存放的是当前 Script 内可访问的 let 变量和 const 变量    ,而 var 变量存放在 Global 上的就不在 Script Scope                    ,它类似于是脚本范围内的全局作用域        。在下面的 demo 中再举例                   。

什么是闭包

闭包指的是那些引用了另一个函数作用域中变量的函数               ,通常是在嵌套函数中实现的            。

比如这个例子:

var globalVariable = 1 const scriptVariable = 2 function test() { let name = Jaychou return { getName() { const count = 1 return name }, setName(newValue) { name = newValue } } } const testFun = test() console.log(testFun.getName()) // Jaychou testFun.setName(小明) console.log(testFun.getName()) // 小明

大家可以根据作用域链的知识,思考一下执行到console.log(testFun.getName())的 getName 里面的时候作用域链是怎样的~

我们用浏览器的开发者工具看一下:

作用域链是当前作用域 》test 函数的闭包 》Script 作用域 》Global 作用域

为什么叫 test 函数的闭包?因为当const testFun = test()的 test 函数执行完之后                 ,test 的函数执行上下文已经被销毁了                  ,但它返回的{ getName(){}, setName(){} }对象被 testFun 引用着   ,而 getName 和 setName 引用着 test 函数内定义的 name 变量              ,所以这些被引用的变量依然需要被保存在内存中                  ,而这些变量的集合称为闭包 Closure; 目前闭包内的 name 变量就只能通过 getName 和 setName 去访问和设置      ,而这也是闭包的作用之一:封装私有变量; 刚才说的 Script Scope 中保存着 scriptVariable 变量           ,globalVariable 变量是 var 声明的                  ,所以在 Global Scope(Window)中    。

再看1个具体案例理解闭包:

const globalCount = 0 function test() { const count = 0 return test1 function test1() { const count1 = 1 return test2 function test2() { const count2 = 2 console.log(test2, globalCount + count + count1 + count2) } } } test()()()

执行到 test2 内部的 console.log 那一行时         ,其作用域链是当前作用域 》test1 的闭包 》test 的闭包 》Script Scope 》Global Scope

闭包使用建议:当不需要使用了之后        ,注意要解除引用着闭包的变量                   ,这样闭包才会被释放                    。比如第1个案例的 testFun 如果不需要用了            ,就把它释放 testFun = null               。

闭包的实际使用案例

封装私有变量

就是刚才的 getName              、setName 案例    ,通过 getName 获取 name                    ,通过 setName 设置 name

封装单例

const Single = (function () { let instance = null return function () { if (!instance) { instance = { name: jaychou, age: 40 } } return instance } })() const obj1 = new Single() const obj2 = new Single() console.log(obj1 === obj2) // true

这里只是举个例子               ,具体的 instance 是什么类型,支持什么功能要看实际项目。

防抖和节流

防抖:

function debounce(fn, delay) { let timer = null; return function () { let context = this; let args = arguments; timer && clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); } }

节流:

function throttle(fn, interval) { let last = 0; return function () { let now = +new Date() if (now - last >= interval) { fn.apply(this, arguments); last = now; } } }

更完整的防抖和节流的实现可参考 Lodash                  ,这里主要是演示闭包的使用

总结

闭包的使用场景很多                  ,功能很强大   ,可以说在前端项目中经常可见例如 React Hooks 等等              ,这里只列举了几个很简单的很实用的应用场景                 。

总结

本文主要介绍了作用域链和闭包                  ,沿着 夯实基础上篇-图解 JavaScript 执行机制 来一起看的话应该比较容易理解      ,若对大家有所帮助           ,请不吝点赞关注~

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
点击量广告投放平台(点击广告如何赚钱-如何选择一个可靠的广告联盟赚钱年赚百万?) 做任务赚钱网站有哪些(做任务赚钱的的平台有哪些-新手小白如何通过悬赏任务平台赚钱)