JavaScript 闭包与作用域:浏览器与 Node.js 的对比

在 JavaScript 编程中,理解闭包和作用域是至关重要的。这两个概念在不同的执行环境中,如浏览器和 Node.js,可能会有所不同。本文将深入探讨这两个概念,并解释它们在浏览器和 Node.js 中的不同之处。

闭包

根据 MDN 的定义,闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

说人话就是函数内部使用了外部的变量就会产生闭包

请看接下来的例子思考

为什么以上段代码在nodejs的环境下产生了闭包?

为什么同样的代码在浏览器环境下没有产生闭包?

以上代码是浏览器正常产生的闭包

作用域

我们首先了解一下什么是作用域,在 JavaScript 中,作用域是一个非常重要的概念,它决定了变量、函数和对象的可访问性。作用域可以是全局的,也可以是局部的,还可以是块级的。根据 MDN 的定义,JavaScript 有两种类型的作用域:全局作用域和局部作用域。一个变量如果在函数外部声明,或者没有使用 var、let 或 const 关键字就赋值,那么它就是全局变量,有全局作用域。如果一个变量在函数内部用 var、let 或 const 关键字声明,那么它就是局部变量,有局部作用域。

浏览器中的作用域

在浏览器中,全局作用域是 window 对象。这意味着在全局作用域中定义的所有变量和函数都会成为 window 对象的属性和方法。在浏览器中,作用域链的顺序是:局部作用域 -> 全局作用域。

javascript 复制代码
console.log(this); // window
console.log(this === window); // true

var name1 = "global name1";
const name2 = "global name2";
console.log(window.name1); // "global name1"
console.log(window.name2); // undefined

function testFunc() {
  console.log(this); // window
}
testFunc();

new Function('console.log(this)')(); // window

在这段代码中,首先我们打印出 this,在全局作用域中,this 指向 window 对象。然后我们声明了两个变量 name1 和 name2,其中 name1 使用 var 关键字声明,name2 使用 const 关键字声明。在浏览器中,使用 var 关键字在全局作用域中声明的变量会成为 window 对象的属性,而使用 const 或 let 关键字声明的则不会。然后我们定义了一个函数 testFunc,在这个函数内部,this 也指向 window 对象。最后,我们使用 new Function 创建了一个新的函数并立即执行,这个新函数的 this 也指向 window 对象。

Node.js 中的作用域

然而,在 Node.js 中,情况就有些不同了。Node.js 不仅有全局作用域,还有模块作用域。每个 Node.js 文件(或称为模块)都有自己的作用域,这就意味着在一个文件中定义的变量和函数在其他文件中是不可见的,除非它们被明确地导出和引入。在 Node.js 中,作用域链的顺序是:局部作用域 -> 模块作用域 -> 全局作用域。

javascript 复制代码
console.log(this); // {}

function testFunc() {
  console.log(this); // global
}
testFunc();

(function() {
  "use strict";
  console.log(this); // undefined
})();

var name1 = "global name1";
console.log(this.name1); // undefined

console.log(this === module.exports); // true

new Function('console.log(this)')(); // global

在 Node.js 中,this 在全局作用域中不再指向全局对象,而是指向当前模块的 exports 对象(如果你没有导出任何内容,那么它就是一个空对象)。在函数内部,this 指向全局对象。在严格模式下的函数内部,this 是 undefined。在 Node.js 中,使用 var 关键字在全局作用域中声明的变量不会成为全局对象的属性。最后,我们使用 new Function 创建了一个新的函数并立即执行,这个新函数的 this 指向全局对象。

结论

在全局作用域中,虽然函数可能引用了外部变量,但这并不会产生我们通常理解的闭包,因为全局变量对所有代码都是可见的,没有被封装或隐藏起来。而在函数或模块作用域中,如果一个函数引用了外部变量,那么这个函数就形成了一个闭包,因为它封装了对外部变量的访问,使得这个变量即使在函数执行完毕后,仍然不会被垃圾回收机制回收。希望这篇文章能帮助你更好地理解这些概念

相关推荐
用户5372312882467 分钟前
你的 WGSL 从未被执行过——一个 secure context 静默陷阱的排查实录
前端
Daniel_12316 分钟前
轻量级站点监控面板 LightPing 开源啦
前端·后端·github
想风17 分钟前
A/B 测试怎么做?独立站转化率优化的完整实操步骤
前端·后端·github
lerhxx23 分钟前
从 Markdown 到生成式 UI:AI 应用中的流式渲染实践
前端·javascript
迅猛龙办公室23 分钟前
python实现简单进度条
java·前端·python
Rosanci33 分钟前
Codex 下载与本地部署实战:从安装到运行全流程指南
开发语言·前端·算法·chatgpt·codex
zhangzeyuaaa38 分钟前
深入 Ruby:Block、Proc、Lambda 核心区别与最佳实践
开发语言·前端·ruby
liangshanbo12151 小时前
前端高级面试题:WebSocket 双向流式通信怎么设计?
前端·websocket·网络协议
可乐鸡翅yeah_1 小时前
新手梳理:HLS 线上问题,哪些该提给 CDN,哪些该找后端切片服务
开发语言·前端·javascript·vue.js·网络协议·http·m3u8在线
zwd20051 小时前
Manim arrange 和 arrange_in_grid 用法详解:buff、aligned_edge、rows/cols(0.21.0 实测)
前端·python·edge