可能重复:
JavaScript 变量范围
我(显然)对 Javascript 很陌生,我在其他线程上看到了关于全局和局部变量使用的几个不同点,但我试图在一个地方锁定一些关于该主题的基本点。
以下在函数外部声明(或在内部使用)有什么区别?
var 事物 = 1;
东西=1;
我了解使用 var 在其当前范围内声明变量。因此,省略 var 使其成为全局变量。有哪些可能出现的陷阱?有人可以举一个简单的例子来说明在这种情况下变量可能会相互影响吗?
提前致谢。
可能重复:
JavaScript 变量范围
我(显然)对 Javascript 很陌生,我在其他线程上看到了关于全局和局部变量使用的几个不同点,但我试图在一个地方锁定一些关于该主题的基本点。
以下在函数外部声明(或在内部使用)有什么区别?
var 事物 = 1;
东西=1;
我了解使用 var 在其当前范围内声明变量。因此,省略 var 使其成为全局变量。有哪些可能出现的陷阱?有人可以举一个简单的例子来说明在这种情况下变量可能会相互影响吗?
提前致谢。
如果您已经在全局范围内,则差异很小,您不必担心
想象一下你有两个for
循环,你想用它们做点什么。
.
for (i = 0; i < elements.length; i++) {
element = elements[i];
for (i = 0; i < items.length) {
item = items[i];
element.add(item);
}
}
这段伪代码可以在许多不同的语言中正常工作。该程序会看到一个内循环和一个外循环,并且可以看出这i
不是指同一件事。
在 JavaScript 中,它们是相同的i
.
那是因为其他语言有块作用域——任何时候你输入新的花括号,程序都会把变量当作新变量来对待。
在 JavaScript 中只有函数作用域,这意味着函数内部的变量被视为新变量,但在控制语句(if
, for
, switch
)内部,它们是具有相同值的相同变量。
所以外循环设置i
为0,然后进入内循环。
内部循环遍历它的所有项目列表,并i
随着它的进行而建立......
然后它回到外部循环,i
仍然等于items.length - 1
......
如果它小于elements.length
然后它添加一个到i
现在更高的比items
长度,所以在内循环中什么也没有发生,不再...
...如果items.length
大于elements.length
而不是,那么外循环只会在一次之后结束。
现在,我相信您可以开始考虑在整个程序中多次使用x
or name
or value
or el
or sum
or i
or or default
or src
or url
or img
orscript
等的时间(甚至数万行),然后您可以开始考虑类似上面循环的情况,如果你尝试用相同的名称调用两个不同的东西,事情可能会出错。
这与 var-fallthrough 的问题相同
如果你有一个函数使用一个名为的变量x
,而另一个函数使用另一个名为 的变量x
,那就太好了……除非你忘记声明变量。
// no problems!
function func1 () { var x = 0; }
function func2 () { var x = "Bob"; }
// big problems!
function func1 () { x = 0; }
function func2 () { x = "Bob"; }
func1
集集window.x = 0;
func2
_window.x = "Bob";
...如果window.x
应该等于 42,为了让其他程序正常工作,现在您有可能拥有 3 个损坏的应用程序,只是因为缺少一些var
s。
不过,它不会立即设置全局变量。它实际上做的是通过函数链。如果您在另一个函数中创建一个函数,那么未声明的 var 将查找其父级的 var,然后是其祖父母的 var,然后是其曾祖父母的
var window
...名称,然后它会在 . 上创建一个具有该名称的名称window
。
function func1 () {
var x = 0;
function func2 () {
var y = 1;
x = 2;
z = 3;
}
func2();
}
当你调用 func1 时,它会将自己设置x
为 0,然后调用 func2。func2 将其自身设置y
为 1。然后将 func1's 设置x
为 2。然后,因为 func2 没有,z
而 func1 没有z
也window
没有z
,所以它设置window.z
为 3。
这只是混乱的开始,为什么它是一个非常非常好的主意,以确保您正在定义需要在该函数内部可用的变量(以及在该函数内部创建的任何函数)......。 ..当您引用预先存在的变量时,请仔细引用它们,并知道该变量在您的代码中应该在哪里(哪个函数定义了它......所以程序将在链上的哪个位置在它到达之前停止寻找window
),以及为什么要从另一个函数内部更改它。
一个常见的陷阱是循环计数器——它们总是被命名i
并且会发生冲突。例子:
function a() {
for (i = 0; i < 5; i++)
b();
}
function b() {
for (i = 0; i < 3; i++)
; // something
// now, the /global/ i is reset to 3
// … and the condition in function a will never be met
}
// so
a();
// is an infine loop instead of executing something 15 times
与许多 C 风格的语言不同,JavaScript 支持块语法但不支持块作用域,因此以下代码可能无法按您的预期运行:
var foo = 'bar';
{
var foo = 'baz';
}
console.log(foo); // 'baz'
这在其他支持块的 JavaScript 结构中也很明显,例如if
:
var foo = 'bar';
if (true) {
var foo = 'baz';
}
console.log(foo); // 'baz'
此外,正如 Bergi 指出的那样,您i
的 s 计数器for
将相互覆盖,因为 afor
不会创建新范围。
Crockford 认为 JavaScript 缺少块作用域是该语言的“糟糕的部分”。相反,JavaScript 有词法作用域,所以只有函数会创建一个新的作用域:
var foo = 'bar';
(function() {
var foo = 'baz';
console.log(foo); // 'baz'
}());
console.log(foo); // 'bar'
JavaScript 中的每个函数都可以访问包含它的函数中的变量;内部可以访问外部,但反之则不行。在上面的示例中,IIFE(立即调用的函数表达式)可以访问foo = 'bar';
全局范围内的定义,但我们选择foo
使用本地var
声明覆盖并将其设置为等于'baz'
。
奖励点:Shog9 发现with
可以使用对象字面量模拟块范围,如下所示:
var foo = 'bar';
with ({foo: 'baz'}) {
console.log(foo); // 'baz'
}
console.log(foo); // 'bar'
Crockford不鼓励with
由于其模棱两可,但如果你真的想要块范围,我认为with
解决方法没有什么害处。