在 w3schools 有写:
如果您声明一个变量,而不使用“var”,则该变量始终变为 GLOBAL。
在函数内部声明全局变量有用吗?我可以想象在某个事件处理程序中声明一些全局变量,但它有什么用呢?更好地使用内存?
在 w3schools 有写:
如果您声明一个变量,而不使用“var”,则该变量始终变为 GLOBAL。
在函数内部声明全局变量有用吗?我可以想象在某个事件处理程序中声明一些全局变量,但它有什么用呢?更好地使用内存?
不,没有 RAM 好处或类似的东西。
w3schools 谈论的是我称之为隐式全局变量的恐怖。考虑这个函数:
function foo() {
var variable1, variable2;
variable1 = 5;
varaible2 = 6;
return variable1 + variable2;
}
看起来很简单,但它返回的是NaN
,而不是11
,因为行中的错字varaible2 = 6;
。它创建了一个带有拼写名称的全局变量:
这是因为该函数分配给varaible2
(注意错字),但未varaible2
在任何地方声明。通过 JavaScript 中作用域链的机制,这最终成为对全局对象(您可以像window
在浏览器上一样访问)上的(新)属性的隐式赋值。
这只是松散模式 JavaScript 的一个“特性”,分配给一个完全未声明的标识符不是错误;相反,它在全局对象上创建一个属性,全局对象上的属性是全局变量。(最多通过ES5,所有全局是全局对象。由于ES2015的,虽然加入了一种新的全球性不是全局对象的属性。全球范围的特性let
,const
以及class
创建新的全球.)
我的例子是一个错字,但当然,如果你愿意,你可以故意这样做。毕竟,它是语言的一个明确定义的部分。所以:
myNewGlobal = 42;
...任何myNewGlobal
未声明的地方都将创建新的全局。
但我强烈建议永远不要故意这样做:它使代码难以阅读和维护,并且当 JavaScript module变得更加普遍和广泛时,这些代码将与 JavaScript module不兼容。如果您确实需要在运行时从函数内部创建全局变量(已经是一个危险信号,但有正当理由),请通过分配给 on window
(或任何引用您环境中的全局对象的属性)来显式执行; 它window
在浏览器上):
window.myNewGlobal = 42;
事实上,我建议使用 ES5 的严格模式。严格模式使分配给未声明的标识符成为错误,而不是默默地创建全局。如果我们一直使用严格模式,foo
上面的问题会更容易诊断:
有点切线,但总的来说,我建议尽可能避免使用全局变量。全局命名空间在浏览器上已经非常非常混乱了。浏览器使用 为 DOM 中的每个元素创建一个全局变量id
,对于大多数具有 的元素name
,并且有几个自己的预定义全局变量(如title
),它们很容易与您的代码发生冲突。
相反,只需为自己定义一个很好的作用域函数并将您的符号放入其中:
(function() {
var your, symbols, here, if_they_need, to_be_shared, amongst_functions;
function doSomething() {
}
function doSomethingElse() {
}
})();
如果你这样做,你可能想要启用严格模式:
(function() {
"use strict";
var your, symbols, here, if_they_need, to_be_shared, amongst_functions;
function doSomething() {
}
function doSomethingElse() {
}
})();
...正如前面提到的,它的优点是将未声明的标识符的赋值转化为错误(以及其他各种有用的东西)。
请注意,在 JavaScriptmodule中(在 ES2015 中添加,但现在才开始流行),默认情况下启用了严格模式。(这也是class
定义的情况,也是 ES2015 中的新内容。)
忘记 var 时的副作用
隐含的全局变量和明确定义的全局变量之间有一个细微的区别。不同之处在于可以使用 delete 运算符取消定义这些变量:
• 不能删除用var 创建的全局变量(在任何函数之外的程序中创建的)。
• 可以删除不带var 创建的隐含全局变量(无论是否在函数内部创建)。
这表明隐含的全局变量在技术上不是真正的变量,但它们是全局对象的属性。可以使用 delete 运算符删除属性,而变量不能:
// define three globals
var global_var = 1;
global_novar = 2; // antipattern
(function () {
global_fromfunc = 3; // antipattern
}());
// attempt to delete
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true
// test the deletion
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"
在 ES5 严格模式下,对未声明变量(例如前面代码段中的两个反模式)的赋值将引发错误。
JavaScript 模式,作者:Stoyan Stefanov (O'Reilly)。版权所有 2010 Yahoo!, Inc.,9780596806750。
全局变量的唯一用途是如果您需要全局访问它们。在这种情况下,您应该var
在函数外部使用关键字来声明它们,以明确表示您确实想要创建全局变量,并且var
在尝试声明局部变量时不要忘记。
通常,您应该尝试限定代码的范围,以便在全局范围内尽可能少地需要。您在脚本中使用的全局变量越多,将其与另一个脚本一起使用的机会就越少。
通常函数中的变量应该是局部的,这样当你退出函数时它们就会消失。
在不使用 var、let 或 const 的情况下在函数内部声明变量并不比在函数内部使用 var、let 或 const 声明该变量有用。而且,正如之前对这个问题的回答所指出的,函数局部的、隐式的全局声明在声明它们的函数范围之外可能会造成混淆和问题。
我想谈谈 w3schools 引用以及之前对这个问题的回答中缺少的一些微妙之处。
首先,如果您从不调用生成隐式全局变量的函数,则不会生成任何隐式全局变量。这与 w3schools 的引用有细微的差别,因为它违背了他们声明中的“始终”部分。
function generateImplicitGlobals(){
x = "x";
window.y = "y";
}
// before calling the generateImplicitGlobals function, we can safely see that the x and y properties of the window object are both undefined:
console.log("before calling the generateImplicitGlobals function, properties x and y of the window object are: " + window.x + " and " + window.y);
// before calling the generateImplicitGlobals function, we can test for the existence of global variables x and y; note that we get errors instead of undefined for both.
try{
console.log("before calling the generateImplicitGlobals function, x is: " + x);
}
catch(e){
console.log("before calling the generateImplicitGlobals function, an attempt to reference some global variable x produces " + e);
}
try{
console.log("before calling the generateImplicitGlobals function, y is: " + y);
}
catch(e){
console.log("before calling the generateImplicitGlobals function, an attempt to reference the global variable b also produces " + e);
}
关于先前答案的微妙之处,一旦调用 generateImplicitGlobals 函数,我们可以看到尝试访问 window.x 属性或全局变量 x 返回相同的值(并且 window.y 属性和全局 y 变量返回相同的值)。当从 generateImplicitGlobals 函数内部或外部调用时,这些语句为真。
function generateImplicitGlobals(){
x = "x";
window.y = "y";
console.log("inside the function, x and window.x are: " + x + " and " + window.x);
console.log("inside the function, y and window.y are: " + y + " and " + window.y);
}
// now, call the generator, and see what happens locally and globally.
generateImplicitGlobals();
console.log("after calling the generateImplicitGlobals function, x, window.x, y, and window.y are: " + x + ", " + window.x + ", " + y + ", and " + window.y);
有时在函数内部创建新的全局可访问属性很有用,以后可以通过引用 window 对象轻松访问这些属性(所有全局声明的属性都附加到 window 对象)。
然而,通常声明任何东西都是全局可访问的,它可能会在以后导致问题,因为这些属性很容易被覆盖等。简单地将值作为参数传递给函数并检索它们的结果要好得多。