【问题标题】:Variable Scope and Var变量范围和变量
【发布时间】:2014-01-31 17:00:31
【问题描述】:

一切都始于这些简单的代码行:

a = 3;
b = 2;

function line(x) {
    var a = 5;
    var b = 4;

    return a*x+b;
}

// returns 17
b = line(a) - b;
alert(b);

// returns 36
c = line(a) + b;
alert(c);

两个警报分别返回 17 和 36。
控件按预期工作。直到……


我改错了
函数内部是这样的:

function line(x) {
    var a = 5; 
        b = 4;

    return a*x+b;
}

突然第 13 行返回 15,第 17 行返回 23
随着我关注var,情况继续恶化
在兔子洞里,随着我的下降而变得更加陷入困境。

我意识到我可以记下始终使用var
并且知道我的代码将始终按预期工作
但这已经成为一个原则问题,现在我需要
了解var 的实际工作原理。

这里是四地狱的链接,
由(并且可能为)我制作:

谜语#1 http://jsfiddle.net/js_test/gNEmY/

谜语#2 http://jsfiddle.net/js_test/FJVYL/

谜语#3 http://jsfiddle.net/js_test/Vz7Sd/

谜语#4 http://jsfiddle.net/js_test/RaA5J/

如果有人能给我见解
了解幕后发生的事情
也就是说,在每次 alert() 调用期间会发生什么;
我真的很感激。

【问题讨论】:

标签: javascript variables closures scope


【解决方案1】:

var 创建一个局部变量,作用域为它出现的函数。在全局范围内声明的任何变量都将成为全局对象的属性(在浏览器中,window),并且在该函数中未使用 var 声明的函数中引用的任何变量均指周围范围,很可能是全局范围。

还有一个新关键字let,即将出现在即将发布的 ECMAScript 版本中,并且已经出现在一些浏览器中,它创建了一个块范围的变量。

因此,在您的第一个示例中(我将假设它在浏览器中运行),您创建了分别绑定到 3 和 2 的 window.awindow.b。函数line 声明局部变量ab。当 window.a 传递给 line 时,参数x 绑定到值 3(window.a 的值)。局部变量 ab 分别为 5 和 4。 这些与window.a和window.b无关。计算a*x+b因此是5*3+4,即19。

代码b = line(a) - bwindow.a 传递给line,计算值19,从中减去window.b 的当前值(2),得到17,然后将其分配给window.b。 36 的值来自line(3)(仍然是 19)加上 17(window.b 的新值)。

当您在函数line 中从对b 的赋值中删除var 时,您对其进行了更改,以使b 不再是局部变量。 在这种情况下,函数中对b 的所有引用都指向全局值window.b。

在你的谜语 #2 的情况下,你得到 23,在第一次调用后 window.b 等于 15。当 line(a) 被第二次调用时,window.b 被设置回 4, a*x+b 计算还是得到19,然后window.b(4)再加一次,得到23。

请务必注意,var 关键字声明了一个函数范围的变量,而不是您对其他 C 派生语言所期望的块范围的变量。例如,在这个函数中:

function scope(array) {
    var a = 7;

    for (var b = 0; b < array.length; ++b) {
        var c = array[b];
    }

    alert(a + b + c);
}

所有变量的作用域都延伸到整个函数。特别是,c 的范围仅限于 for 循环。

但是,未在特定范围内声明的变量不一定引用全局变量。如果它嵌套在另一个函数范围内,它将引用该嵌套范围。考虑以下示例:

var b = 7;

function outer() {
    var b = 42;

    function inner() {
        return b; // refers to b in outer, not global
    }

    return inner();
}

alert(outer()); // 42, not 7

【讨论】:

  • 这是一种更好的表达方式。我只是说 b 没有在inner 中声明,而不是根本没有声明。
  • 非常感谢@DavidConrad
【解决方案2】:

没有var声明的变量是全局变量,这是规则。 但请记住,如果您想用一个var 声明多个局部变量,请用逗号分隔它们。

var a = 5; // local variable
    b = 4; // global varialbe

var a = 5, // local variable
    b = 4; // local varialbe

【讨论】:

  • “没有var 声明的变量”是一个非sequor。变量只能使用var 声明(尽管在函数声明的形式参数中包含标识符实际上是变量声明)。
【解决方案3】:

VAR 在内存中为新变量声明一个新实例。简单地说b = 4 将尝试将b 的值更改为4。如果b 没有被初始化,什么都不会发生。

这与PRIVATELOCAL 变量的概念以及命名约定有关。

首先,您有 2 个名为 a 的变量和 2 个名为 b 的变量。这是一种不好的做法,因为您可能会错误地更改另一个的值,或者它的旧值可能仍然存在于内存中,因为它已经被初始化并被赋予了一个值。请记住,您并不总是需要在初始化时分配一个值,这只是最佳实践。

其次,您的函数可以修改上一级的变量。换句话说,通过去掉那一行上的VAR,就可以修改您在文件开头设置为2b 的原始值。

TLDR; 不要多次使用同一个变量名,并且在尝试创建新变量时始终使用您的 VAR。

【讨论】:

  • 这不是“取决于你的编译器的规则”,而是 Javascript 的规则。如果您有嵌套函数,并且外部函数声明了一个变量,而内部函数在没有声明它的情况下引用了它,那么它“从它上面的级别”访问那个。
  • 我的错误。我已经修好了。谢谢。
【解决方案4】:

如果您在全局范围内,则没有区别。

如果你在一个函数中,那么“var”将创建一个局部变量,“no var”将查找作用域链,直到找到变量或到达全局作用域(此时它将创建它)。

Source

对于您的第一个函数,首先处理line(a),在它运行时,您已经设置了b = 4。因此,由于b 是一个全局定义的变量,当它运行line(a) - b 时,您会得到:

line(a)(返回 19)

减去b (b = 4)

19 - 4 = 15

您可以简化您的陈述并获得相同的结果:

//define global variable b
var b = 2;
function line(x) {
    //x = 3
    //update global variable b; it now = 4
    b = 4;
    //forget the math and just return 19
    return 19;
}

//run function; before, b = 2, after b = 4
b = line(3) - b;
alert(b);

与您的第二个示例类似,因为您的函数更改了全局变量 b 的值,唯一的区别是您添加 4 而不是减去 (19 + 4 = 23)。

为避免变量的这种混淆性质,请始终小心区分全局变量和局部变量,并且仅在需要显式更新函数中的全局变量时删除var。事实上,由于全局变量始终保留在内存中,因此只有在明确需要它们时才应使用全局变量。这是一个明确的例子:

var globalVar;
function someFunc(x) {
    var tempVar = 4;
    return x * tempVar;
}

【讨论】:

  • 谢谢@r3mus 我明白了。你介意看看Riddle #2 我仍然很困惑当我在c = line(a) + b; 之后打电话给alert(a); 会发生什么
  • 再次,您更新了a,使其现在 = 4(您在第一次调用 line(a) 时执行此操作。当您第二次运行它时,@987654338 的值@ 不同 (= 5)。
  • 我了解local 在省略var 时如何更新global 变量。但是,关于Riddle #2,如果b 在第二个alert 呼叫之前变为b = line(a) - b,我很困惑c = line(a) + b 如何return 23?为什么c = line(a) + b 会使用local 两次(例如5*3+4+4)而不是使用局部变量并更新global(例如5*3+4 + b = line(a) - b;)?
  • var 仅在您在函数中时创建局部变量;在这种情况下,因为您不支持 b,所以当您再次运行该函数时,您将 b 的值更改回 4。它与 19 - 4 = 15 相同,但由于您要添加 @ 987654354@这次是19 + 5 = 23
猜你喜欢
  • 2013-05-26
  • 2017-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
相关资源
最近更新 更多