我不断看到有关JavaScript中不要使用全局变量的警告,但似乎人们这么说的唯一原因是因为它会堵塞全局命名空间。我可以想象通过将所有变量放入一个大对象中来轻松解决这个问题。现在问题是:除了方便之外,是否还有其他原因不使用全局变量?它们是否涉及性能或兼容性问题?
我不断看到有关JavaScript中不要使用全局变量的警告,但似乎人们这么说的唯一原因是因为它会堵塞全局命名空间。我可以想象通过将所有变量放入一个大对象中来轻松解决这个问题。现在问题是:除了方便之外,是否还有其他原因不使用全局变量?它们是否涉及性能或兼容性问题?
全局变量会占用全局命名空间,查找速度比局部变量慢。
首先,拥有许多全局变量总是不好的,因为很容易忘记在某个地方声明了一个变量,并意外地在其他地方重新声明它。如果你的第一个变量是局部的,那么你就没有问题。如果它是全局的,那么它刚刚被覆盖了。当你进入暗示全局变量时(例如当你说someVar = someValue
而没有使用var
关键字声明someVar时),这种情况会变得更糟。
其次,全局变量的查找时间比局部变量长。速度上的差异并不是特别大,但确实存在。
有关更深入的阅读和解释全局变量为何被视为不良做法的内容,您可以查看此页面。
全局变量会显著增加耦合,从而严重降低代码的可扩展性和可测试性。一旦使用全局变量,您现在必须知道变量在哪里和如何被修改(即破坏封装)。大部分文献和惯例都会认为在使用全局变量时性能是最不用担心的问题。
这是一篇精彩的文章,概述了全局变量为什么会导致麻烦。
var focus = false;
const elem = document.querySelector('#name');
elem.addEventListener('focus', _ => { focus = true; show(); });
elem.addEventListener('blur', _ => { focus = false; show(); });
function show() { console.log('focus:', focus); }
<input id="name" type="text">
focus
的全局变量,但它覆盖了window
上的focus函数。如果我以后包含一个调用window.focus
的库,它将失败。window
中,因此有可能您会命名某些与浏览器后来相冲突的内容。if (!window.MyAPI) {
window.MyAPI = ...
}
...
MyAPI.doTheThing();
window.MyAPI = window.MyAPI || ...
...
MyAPI.doTheThing();
MyAPI
的新API,这种情况下检查将不会替换全局变量,doTheThing
也将不存在。Report
的类或函数,它试图像上面那样全局和有条件地安装。当浏览器尝试发布Reporting API
时,使用该库的每个站点都会崩溃。(poor), don't conditionally assign the global
window.MyAPI = window.MyAPI || ... // bad!
window.MyAPI = ... // still bad but better
In this case you'll at least override the new browser API with your own. Of course now you have the issue that some other library may want to use the new API end you've effectively hidden it.
(good), Don't use globals.
上面的模式可以说是ES6模块之前遗留下来的。现在,把你的库放到一个模块中可能更好。
然后你就可以将它导入到本地或至少模块级变量中。
import MyAPI from './MyAPI.js';
MyAPI.doTheThing()
MyAPI
是和不是全局变量。它只能被当前模块访问,所以对于该模块来说它是全局的,但对于其他模块则不是。window
。此外,添加到全局对象的属性也会成为全局变量。console.log('foo' in window); // prints false
console.log('foo' in window); // prints true
var foo = 123;
window.bar = 456;
console.log(bar); // prints 456
(function(){
var foo = 'foo',//Local
bar = 'bar';//Local
window.globalVar = foo + bar;//Global
})();
alert(foo);//Error
alert(bar);//Error
alert(globalVar );//'foobar'
你创建的全局变量可能会覆盖现有的窗口对象属性。因为全局变量是在全局上下文中访问,即window对象。