在w3schools上有这样的写道:
如果你声明一个变量,而没有使用“var”,那么这个变量总是会变成全局变量。
在函数内部声明全局变量有用吗?我可以想象在某些事件处理程序中声明一些全局变量,但这有什么好处呢?更好地利用RAM?
在w3schools上有这样的写道:
如果你声明一个变量,而没有使用“var”,那么这个变量总是会变成全局变量。
在函数内部声明全局变量有用吗?我可以想象在某些事件处理程序中声明一些全局变量,但这有什么好处呢?更好地利用RAM?
不,没有RAM的好处或其他类似的好处。
w3schools所谈论的是我所谓的隐式全局变量的恐怖。考虑以下函数:
function foo() {
var variable1, variable2;
variable1 = 5;
varaible2 = 6;
return variable1 + variable2;
}
看起来很简单,但它返回NaN
而不是11
,这是因为在varaible2 = 6;
行上有一个打字错误。并且它创建了一个名字错别字的全局变量:
function foo() {
var variable1, variable2;
variable1 = 5;
varaible2 = 6;
return variable1 + variable2;
}
console.log(foo()); // NaN
console.log(varaible2); // 6?!?!?!
这是因为该函数给varaible2
赋值(请注意错别字),但varaible2
没有在任何地方声明。通过 JavaScript 中作用域链的机制,这最终会成为全局对象上一个(新的)属性的隐式赋值(您可以在浏览器中使用 window
访问它,或在所有现代环境中使用 globalThis
访问)。
这只是松散模式 JavaScript 的“功能”,对完全未声明的标识符进行赋值不会出错;相反,它会在全局对象上创建一个属性,全局对象上的属性是全局变量。(直到 ES5,所有全局变量都是全局对象的属性。自 ES2015 起,另一种新型全局变量被添加到了其中,它不是全局对象的属性。全局范围的 let
、const
和 class
创建了新类型的全局变量。)
我的示例只是一个错别字,但是如果您愿意,您当然也可以故意这样做。但我强烈建议不要这样做。相反,我建议始终使用严格模式,直接或使用 ECMAScript 模块(ESM,在 ES2015 中添加的 JavaScript 的模块系统),默认情况下它们是严格的。严格模式使对未声明的标识符进行赋值变成错误,而不是默默地创建全局变量。如果我们在使用严格模式,上面的 foo
函数的问题会更加明显和容易诊断:
"use strict"; // Turns on strict mode for this compilation unit
function foo() {
var variable1, variable2;
variable1 = 5;
varaible2 = 6; // <=== ReferenceError
return variable1 + variable2;
}
console.log(foo());
建议尽量避免全局变量。在浏览器中,全局命名空间已经非常拥挤,这使得很容易意外创建冲突。浏览器为DOM中的每个元素创建一个全局变量,包括大多数具有name
属性的元素,并具有其自己的几个预定义全局变量(例如name
和title
),这些变量与您的代码很容易发生冲突。
取而代之的是使用JavaScript模块(ESM)。模块中的顶级声明不是全局变量,它们仅对该模块中的代码全局可见。然后,您可以使用export
故意公开要让其他模块能够使用的代码部分(通过import
)。
在2022年,几乎总是可以使用ESM;它得到现代浏览器和Node.js的广泛支持。如果您必须针对不支持它的过时环境(例如Internet Explorer),则可以使用打包程序将您的ESM代码编译成一个捆绑包。
如果出于某种原因无法使用ESM,则可以像在模块标准化之前一样使用一个作用域函数将代码包装起来:
(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() {
}
})();
正如之前提到的那样,使用该方法可以将对未声明标识符的赋值转换为错误(以及其他一些有用的功能)。
如果你必须将某些内容设置为全局变量,可以将其赋值给window
上的属性。(在现代环境中,我会建议你将其赋值给globalThis
上的属性,但如果不能使用ESM,则很可能你所针对的环境不支持globalThis
。)
忘记使用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 Patterns,Stoyan Stefanov著(O'Reilly)。版权所有2010年Yahoo!Inc.,ISBN 9780596806750。
while (true);
是一个很好的例子。 - T.J. Crowder在函数内部声明变量而不使用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);
Uncaught ReferenceError: foo is not defined
。
function noVars(a1,/*vars=*/v1,v2,v3) {
if (noVars.lastA1===a1) return noVars.lastAnswer;
noVars.lastA1=a1;
v1=a1*a1;
v2=v1*v1;
v3=v2*v2*v2;
noVars.lastAnswer = a1+v1+v2+v3;
return noVars.lastAnswer;
}
var noVars = function (a1,/*vars=*/v1,v2,v3) {
if (noVars.lastA1===a1) return noVars.lastAnswer;
noVars.lastA1=a1;
v1=a1*a1;
v2=v1*v1;
v3=v2*v2*v2;
noVars.lastAnswer = a1+v1+v2+v3;
return noVars.lastAnswer;
};
global_novar
的“number”,但我刚刚在那里进行了测试,并获得了与答案中显示的相同结果。我建议使用alert(delete global_novar);
- 它会返回true
还是false
?如果返回false
,则您所做的事情与此答案中显示的不同。 - ToolmakerSteve