为什么一般来说,最好保持网站的全局 JavaScript 作用域不变,并且永远不要触及它?
主题
JavaScript
在GitHub上编辑
总结
在浏览器中执行的 JavaScript 可以访问全局作用域(window
对象)。一般来说,不污染全局命名空间是一个很好的软件工程实践,除非你正在处理一个真正需要全局的特性——整个页面都需要它。避免触及全局作用域的几个原因:
- 命名冲突:在脚本之间共享全局作用域可能导致冲突和错误,当引入新的全局变量或进行更改时。
- 全局命名空间混乱:保持全局命名空间最小化可以避免使代码库难以管理和维护。
- 作用域泄漏:在闭包或事件处理程序中无意中引用全局变量可能导致内存泄漏和性能问题。
- 模块化和封装:良好的设计促进将变量和函数保持在其特定作用域内,从而增强组织性、可重用性和可维护性。
- 安全问题:全局变量可被所有脚本访问,包括潜在的恶意脚本,这会带来安全风险,特别是如果敏感数据存储在其中。
- 兼容性和可移植性:过度依赖全局变量会降低代码的可移植性,并降低与其他库或框架的集成难度。
遵循这些最佳实践以避免全局作用域污染:
- 使用局部变量:使用
var
、let
或const
在函数或代码块内声明变量以限制其作用域。 - 将变量作为函数参数传递:通过将变量作为参数传递而不是全局访问它们来保持封装。
- 使用立即调用函数表达式(IIFE):使用 IIFE 创建新作用域以防止将变量添加到全局作用域。
- 使用模块:使用模块系统封装代码以保持独立的作用域和可管理性。
什么是全局作用域?
在浏览器中,全局作用域是顶级上下文,变量、函数和对象可以从代码中的任何位置访问。全局作用域由 window
对象表示。在任何函数或代码块(即不在任何模块内)之外声明的任何变量或函数都会添加到 window
对象中,并且可以在全局范围内访问。
例如:
// 假设这在全局作用域中运行,而不是在模块中。var globalVariable = '我是全局变量';function globalFunction() {console.log('我是一个全局函数');}console.log(window.globalVariable); // '我是全局变量'window.globalFunction(); // '我是一个全局函数'
在此示例中,globalVariable
和 globalFunction
被添加到 window
对象中,并且可以从全局上下文中的任何位置访问。
全局作用域的陷阱
一般来说,不污染全局命名空间是一个很好的软件工程实践,除非你正在处理一个真正需要全局的特性——整个页面都需要它。有很多原因可以避免触及全局作用域:
- 命名冲突:全局作用域在网页上的所有脚本之间共享。如果你引入新的全局变量或修改现有的全局变量,你可能会导致与在同一页面上使用的其他脚本或库发生命名冲突。这可能导致意外行为和难以调试的问题。
- 全局命名空间混乱:全局命名空间应保持尽可能干净和最小。添加不必要的全局变量或函数会使命名空间混乱,并使代码库随着时间的推移更难于管理和维护。
- 作用域泄漏:在使用闭包或事件处理程序时,很容易意外地创建对全局变量的无意引用,从而导致内存泄漏和性能问题。通过完全避免全局变量,你可以防止这些类型的作用域泄漏。
- 模块化和封装:良好软件设计原则之一是模块化和封装。通过将变量和函数保留在其各自的作用域内(例如,模块、函数或块作用域),你可以促进更好的代码组织、可重用性和可维护性。
- 安全问题:全局变量可以被页面上运行的任何脚本访问和修改,包括潜在的恶意脚本。网站加载第三方脚本是很常见的,如果有人网络被入侵,这可能会带来安全风险,特别是如果敏感数据存储在全局变量中。但是,首先你不应该在客户端上暴露任何敏感数据。
- 兼容性和可移植性:通过严重依赖全局变量,你的代码变得不太可移植,并且更依赖于编写它的特定环境。这可能会使它更难以与其他库或框架集成,或者在不同的环境中运行代码(例如,服务器端与浏览器)。
这是一个使用全局作用域的例子。
// 假设这在全局作用域中运行,而不是在模块中。let count = 0;function incrementCount() {count++;console.log(count);}function decrementCount() {count--;console.log(count);}incrementCount(); // 输出:1decrementCount(); // 输出:0
在此示例中,count
、incrementCount
和 decrementCount
在全局作用域中定义。页面上的任何脚本都可以访问和修改 count
,以及 window
上的所有变量。
避免全局作用域污染
到目前为止,我们希望您相信在全局范围内定义变量不是一个好主意。为了避免污染全局范围,建议遵循最佳实践,例如:
- 使用局部变量:在函数或代码块内声明变量,以限制其作用域,防止它们被全局访问。使用
var
、let
或const
在特定作用域内声明变量,确保它们不会被意外地设置为全局变量。 - 将变量作为函数参数传递:不要直接从外部作用域访问变量,而是将它们作为参数传递给函数,以保持封装并避免全局作用域污染。
- 使用模块:利用模块系统来封装你的代码,防止全局作用域污染。每个模块都有自己的作用域,这使得管理和维护你的代码更容易。
- 使用立即调用函数表达式(IIFE):如果模块不可用,将你的代码包装在 IIFE 中以创建一个新的作用域,防止变量被添加到全局作用域,除非你明确地暴露它们。
// Assuming this is run in the global scope, not within a module.(function () {let count = 0;window.incrementCount = function () {count++;console.log(count);};window.decrementCount = function () {count--;console.log(count);};})();incrementCount(); // Output: 1decrementCount(); // Output: 0
在这个例子中,count
在全局范围内是不可访问的。它只能通过incrementCount
和decrementCount
函数访问和修改。这些函数通过将它们附加到window
对象来暴露给全局范围,但它们仍然可以访问其父范围中的count
变量。这提供了一种封装底层数据并仅公开必要操作的方法——不允许直接操作该值。