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

结论

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

相关推荐
qq_3643717227 分钟前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森1 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
y先森6 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy6 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189116 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿8 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡9 小时前
commitlint校验git提交信息
前端
虾球xz9 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇9 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒9 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript