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

结论

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

相关推荐
MiyueFE几秒前
bpmn-js 源码篇7:Featrues 体验优化与功能扩展(二)
前端
好_快15 分钟前
Lodash源码阅读-isPrototype
前端·javascript·源码阅读
315356691330 分钟前
manus邀请码申请手把手教程
前端·后端·面试
烂蜻蜓1 小时前
HTML 编辑器推荐与 VS Code 使用教程
前端·python·编辑器·html
小画家~2 小时前
第五十九:子传父 defineEmits
前端·javascript·vue.js
html组态2 小时前
组态软件在物联网中的应用概述
前端·物联网·编辑器·html·iot
咖啡虫2 小时前
react中的useContext-弊端(二)
前端·javascript·react.js
懒人村杂货铺2 小时前
react精简面试题
前端·react.js·前端框架
青红光硫化黑2 小时前
React基础之tsx语法
前端·javascript·react.js
好_快2 小时前
Lodash源码阅读-isEmpty
前端·javascript·源码阅读