函数级作用域和块级作用域的区别

11

我已经使用jQuery几个月了,主要使用JavaScript进行编程。 我理解闭包并且已经使用过它们,但是,我仍然无法理解其他语言(如C#)中的函数级别作用域和块级别作用域之间的区别。我一直试图自学,但在这个主题上没有结果。 有人能用一些简单的例子来解释一下吗?

3个回答

22

在ES6(JavaScript的当前版本)之前,JavaScript只有函数级作用域。也就是说,以下代码:

function foo() {
    console.log('before block: ' + bar);      // prints 'undefined'
    if(true) {
        var bar = 1;
        console.log('inside block: ' + bar);  // prints 1
    }
    console.log('outisde block: ' + bar);     // prints 1
}

完全等同于:

function foo() {
    var bar;
    console.log('before block: ' + bar);      // prints 'undefined'
    if(true) {
        bar = 1;
        console.log('inside block: ' + bar);  // prints 1
    }
    console.log('outisde block: ' + bar);     // prints 1
}

事实上,我刚刚展示的称为“提升”,这正是JavaScript所做的:所有变量声明都被提升到函数顶部;而赋值留在原地。

相比之下,像C#这样的语言具有块级作用域。这将导致编译错误:

public void Foo() {
    if(true) {
        var foo = 1;
        Console.WriteLine("inside block: " + foo);
    }
    Console.WriteLine("outside block: " + foo);  // WILL NOT COMPILE
}

但是你可以拥有这个:

public void Foo() {
    var foo = 1;
    if(true) {
        foo = 2;
        Console.WriteLine("inside block: " + foo);  // prints 2
    }
    Console.WriteLine("outside block: " + foo);     // prints 2
}

我想我在这里明白了。谢谢。 - Dimt
很高兴能对你有所帮助,Dimt。如果您认为这是最佳答案,能否请将其标记为已接受? - Ethan Brown
2
@EthanBrown 你误读了 OP 的评论 - 在 内部函数 中声明的变量无法访问外部作用域。 - Alnitak
1
没错,Alnitak,你发现得很好。Dimt,你说的(“在JS中,如果一个变量在内部函数中被声明,封闭函数就可以访问该变量”)是不正确的;函数内任何位置的变量声明都会掩盖外部作用域中同名的变量声明。 - Ethan Brown
1
@uzay95,我不确定我理解你的问题,但是var bar = 1既是初始化又是声明。声明将被提升,而初始化则不会。此外,我不建议将w3schools作为学习资源;他们擅长SEO,但其他方面并不出色。 - Ethan Brown
显示剩余6条评论

5
function scopeTest() {

/* consider this simple for loop
    to be the "block" that we were
    talking about earlier
*/
for (var i = 0; i <= 5; i++)
{
  var inFor = i; 
}

alert(inFor);  // what happens here?

}


// call the function defined above
scopeTest( );

在上面的代码中,我们有一个叫做inFor的变量,它是在for循环中声明的。然后我们试图在alert语句之外访问inFor变量。
如果上面的代码没有触发任何alert,则说明Javascript使用块级作用域。在块级作用域语言中,变量inFor将不会在for循环之外可见。这意味着如果Javascript是块级作用域语言,则调用"alert(inFor);"将无法识别inFor变量,并且不会输出到一个提示框中。
但是,上面的代码实际上输出了一个"5",这意味着inFor变量确实存在于for循环之外,这必须意味着Javascript没有块级作用域。这就是我们的答案 - Javascript没有块级作用域。
function scopeTest() {

var x = 2;

//this is always true:
if(x == 2)
{
  var y = 15;
  for (var i = 0; i <= 5; i++)
  {
    var inFor = i; 
  }
} 

  console.log(y); // y is defined, prints 15   
  console.log(i);  // i is defined, prints 6
  console.log(inFor);  // inFor is defined, prints 5

}

您可以在上面的代码中看到,变量y、i和inFor要么在if语句内部声明,要么在for循环内部声明。但是,即使这些变量在不同的“块”内声明,它们仍然对函数的其余部分可见。这是因为所有这些变量都是在同一个函数内声明的 - 这就是函数作用域的全部内容。 块作用域 vs 函数作用域 那么,如果Javascript不使用块作用域,它使用什么样的作用域呢?
嗯,Javascript使用一种叫做函数作用域的东西。
基本上,函数作用域和块作用域之间的区别在于,在使用函数作用域的语言中,任何在函数内声明的变量都可以在该函数内的任何地方看到。但是在块作用域中,变量的可见性被限制在由大括号包围的任何给定块内(无论它是if语句,where/for循环等)。 http://www.programmerinterview.com/index.php/javascript/javascript-block-scope/ http://www.programmerinterview.com/index.php/javascript/javascript-function-scope/
    {
     here you can't access both a and b
     var a=1
     here you can access only a
        {
        here you can access only a
        var b=3
        here you can access both a and b
        {
        here you can access both a and b
        }
        here too you can access both a and b
        }
        here you can access only a
        }
       here you can't access both a and b

所以在块级作用域中,For循环中的迭代变量无法在外部块中访问,而在函数级作用域中,它可以在外部函数中可见。这正确吗? - Dimt
不,看{}这是变量的边界,所以你不能访问此边界之外的任何变量...请注意...{}内部包含变量的声明... - vino20
请查看我的更新答案...希望你现在能理解了 - vino20
我会尝试这些例子来练习,这应该会对我有所帮助。谢谢。 - Dimt

0
继续@Ethan Brown的回答,如果我们使用let或const代替var,我们会得到“referenceError”,因为let和const是块作用域。
function foo() {
    console.log('before block: ' + bar);      // ReferenceError: bar is not defined
    if (true) {
        let bar = 1;                          // bar is not let and not var
        console.log('inside block: ' + bar);  // prints 1
    }
    console.log('outisde block: ' + bar);     // ReferenceError: bar is not defined
}

只是想让答案更完整。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接