Javascript 变量范围与Var

Javascript 变量范围与Var,javascript,variables,closures,scope,Javascript,Variables,Closures,Scope,这一切都是从以下几行简单的代码开始的: 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 li

这一切都是从以下几行简单的代码开始的:

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

谜语2

谜语3

谜语4

如果有人能给我一些启示
在引擎盖下发生了什么
也就是说,在每个alert()调用过程中发生了什么;

非常感谢。

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

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

var a = 5, // local variable
    b = 4; // local varialbe
如果你在全球范围内,那么没有区别

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

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

行(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;
}

VAR在内存中为新变量声明一个新实例。简单地说
b=4
将尝试将
b
的值更改为
4
。如果
b
尚未初始化,则不会发生任何情况

这与
PRIVATE
LOCAL
变量的概念以及命名约定有关

首先,有两个变量称为
a
,还有两个变量称为
b
。这是一种糟糕的做法,因为您可能会错误地更改另一个的值,或者它的旧值可能仍然存在于内存中,因为它已经初始化并给定了一个值。记住,初始化时并不总是需要赋值,这只是最佳实践

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

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

VAR
创建一个局部变量,其作用域为该变量出现的函数。在全局范围中声明的任何变量都将成为全局对象的属性(在浏览器中,
窗口
),在函数中引用的任何变量,如果在该函数中未使用
var
声明,则会指代周围的范围,很可能是全局范围

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

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

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

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

在你的谜语#2的例子中,你得到了23,在第一个呼叫窗口之后。b等于15。第二次调用第(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);
}
可能重复感谢@Bergi I u
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