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 指向全局对象。

结论

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

相关推荐
web守墓人36 分钟前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L42 分钟前
CSS知识复习5
前端·css
许白掰42 分钟前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器
中微子5 小时前
🔥 React Context 面试必考!从源码到实战的完整攻略 | 99%的人都不知道的性能陷阱
前端·react.js
中微子6 小时前
React 状态管理 源码深度解析
前端·react.js
加减法原则7 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele8 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4538 小时前
React移动端开发项目优化
前端·react.js·前端框架
你的人类朋友8 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
web_Hsir8 小时前
vue3.2 前端动态分页算法
前端·算法