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

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

闭包

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

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

请看接下来的例子思考

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

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

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

作用域

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

浏览器中的作用域

在浏览器中,全局作用域是 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 对象。然后我们声明了两个变量 name1name2,其中 name1 使用 var 关键字声明,name2 使用 const 关键字声明。在浏览器中,使用 var 关键字在全局作用域中声明的变量会成为 window 对象的属性,而使用 constlet 关键字声明的则不会。然后我们定义了一个函数 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 指向全局对象。在严格模式下的函数内部,thisundefined。在 Node.js 中,使用 var 关键字在全局作用域中声明的变量不会成为全局对象的属性。最后,我们使用 new Function 创建了一个新的函数并立即执行,这个新函数的 this 指向全局对象。

结论

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

相关推荐
dy171741 分钟前
element-plus表格默认展开有子的数据
前端·javascript·vue.js
2501_915918414 小时前
Web 前端可视化开发工具对比 低代码平台、可视化搭建工具、前端可视化编辑器与在线可视化开发环境的实战分析
前端·低代码·ios·小程序·uni-app·编辑器·iphone
程序员的世界你不懂5 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
索迪迈科技5 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育
gnip5 小时前
JavaScript二叉树相关概念
前端
attitude.x6 小时前
PyTorch 动态图的灵活性与实用技巧
前端·人工智能·深度学习
β添砖java6 小时前
CSS3核心技术
前端·css·css3
空山新雨(大队长)6 小时前
HTML第八课:HTML4和HTML5的区别
前端·html·html5
猫头虎-前端技术7 小时前
浏览器兼容性问题全解:CSS 前缀、Grid/Flex 布局兼容方案与跨浏览器调试技巧
前端·css·node.js·bootstrap·ecmascript·css3·媒体