我(显然)对Javascript很新,我在其他线程上看到了关于Global和Local变量使用的几个不同点,但我试图在一个地方锁定一些关于这个主题的基本点
在函数外部(或在函数内部)声明的以下内容之间有什么区别?
var thing = 1;
thing = 1;
我知道使用var会在其当前范围内声明变量。因此,将var视为全局。可以出现哪些陷阱?有人能给出一个简单的例子,说明在这种情况下变量可能会相互衔接吗?
提前致谢。
答案 0 :(得分:2)
如果您已经在全球范围内,差异非常小,您不必担心它
假设您有两个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中只有函数作用域,这意味着函数变量内部被视为new,但在控制语句(if
,for
,switch
)内部,它们是具有相同值的相同变量
因此外部循环将i
设置为0,然后进入内部循环
内部循环遍历其所有项目列表,并在其进行时构建i
...
然后它返回到外循环,i
仍然等于items.length - 1
...
如果它小于elements.length
,那么它会向i
添加一个,现在高于items
长度,因此内循环中没有任何事情发生了......
...如果items.length
大于elements.length
,那么外部循环只会在一次结束后结束。
现在,我确信您可以开始考虑您可能想要使用x
或name
或value
或el
或{{1在您的整个计划中数次(或数千)或sum
或i
或default
或src
或url
或img
等多次甚至是行,你可以开始考虑上面循环的情况,如果你尝试用同一个名字调用两个不同的东西,那么事情可能会出错。
这与var-fallthrough相同的问题
如果你有一个使用名为script
的变量的函数和另一个使用另一个名为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";
应该等于42,那么其他一些程序可以正常工作,现在你有可能有3个破坏的应用程序,只是因为缺少window.x
个。
但它并没有立即设置全局变量。它实际上做的是通过功能链。如果你在另一个函数中创建一个函数,那么一个未声明的var将会查看其父级的变量,然后是它的祖父母的变量,然后是它的曾祖父母的变量......
如果它一直到var
并且没有人拥有该名称的var,那么它会在window
上创建一个具有该名称的名称。
window
当你调用func1时,它会将自己的function func1 () {
var x = 0;
function func2 () {
var y = 1;
x = 2;
z = 3;
}
func2();
}
设置为0,并调用func2。
func2将自己的x
设置为1。
然后它将func1的y
设置为2。
然后,因为func2没有x
且func1没有z
且z
没有window
,所以它将z
设置为3。
这只是混乱的开始,为什么它是一个非常非常好的想法,以确保你定义需要在该函数内部(以及在该函数内部创建的任何函数)中可用的变量。 ..
...当你引用预先存在的变量时,你仔细地引用它们,并在你的代码中知道变量应该在哪里(哪个函数定义了它......所以在程序链的哪个链上)在它到达window.z
之前停止查看,以及为什么要从另一个函数内部更改它。
答案 1 :(得分:1)
一个常见的陷阱是循环计数器 - 它们总是被命名为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
答案 2 :(得分:0)
与许多C风格的语言不同,JavaScript支持块语法,但不支持块范围,因此以下内容可能无法按预期运行:
var foo = 'bar';
{
var foo = 'baz';
}
console.log(foo); // 'baz'
在其他支持if
等块的JavaScript构造中也很明显:
var foo = 'bar';
if (true) {
var foo = 'baz';
}
console.log(foo); // 'baz'
此外,正如Bergi指出的那样,i
的{{1}}计数器将相互覆盖,因为for
不会创建新的范围。
Crockford认为JavaScript缺少块范围{+ 3}}。相反,JavaScript有"Awful Part",因此只有函数才会创建一个新范围:
for
JavaScript中的每个函数都可以访问包含它的函数中的变量;内部可以访问外部,但反之亦然。对于上面的示例,lexical scoping(立即调用的函数表达式)可以访问全局范围中定义的var foo = 'bar';
(function() {
var foo = 'baz';
console.log(foo); // 'baz'
}());
console.log(foo); // 'bar'
,但我们选择使用本地覆盖foo = 'bar';
foo
声明并将其设置为var
。
奖励点:Shog9发现'baz'
可以使用像这样的对象文字来模拟块范围:
with
Crockford IIFE由于其含糊不清,但如果您真的希望阻止范围,我认为var foo = 'bar';
with ({foo: 'baz'}) {
console.log(foo); // 'baz'
}
console.log(foo); // 'bar'
解决方法没有任何危害。