JavaScript基础混乱

JavaScript fundamentals confusion

本问题已经有最佳答案,请猛点这里访问。

嗨,我正在努力理解JavaScript的基本原理,并坚持一个条件。

1
2
3
4
5
6
7
8
    var foo = 1;
    function bar(){
        foo = 10;      
        return;
        function foo(){}
    }
    bar();
    alert(foo);

这里,alert(foo)将给我1,我知道在返回语句之后,函数foo()将不会执行。但现在,如果更改代码:

1
2
3
4
5
6
7
    var foo = 1;
    function bar(){
        foo = 10;      
        return;
    }
    bar();
    alert(foo);

在bar函数中,如果我要删除函数foo()。那么alert(foo)会给我10

请帮忙,如果有人能解释我为什么?


这叫做javascript提升

我会尽量详细解释的。这就是我们拥有的

1
2
3
4
5
6
7
8
9
10
var foo = 1;

function bar(){
  foo = 10;      
  return;
  function foo(){}
}

bar();
alert(foo);

口译员将把这个改写为

1
2
3
4
5
6
7
8
9
10
var foo = 1;

function bar(){
  function foo(){}  // this is also equal to var foo = function(){};
  foo = 10;      
  return;      
}

bar();
alert(foo);

所以现在解释一下提升的代码。

1
2
3
4
5
6
7
8
9
10
var foo = 1; // global variable;

function bar(){
  var foo = function(){};  // foo is local variable of type function
  foo = 10;                // foo is changed to hold a number
  return;      
}

bar();
alert(foo);  // you alert global variable.

如您所见,如果代码function foo(){}存在,它在bar()范围内被视为局部变量,对foo的任何更改都被视为局部变量更改。

  • 当你的bar()中有function foo(){}时,你甚至没有触及全局变量。因此提醒1

  • 当你没有function foo(){}时,你会碰到全局变量,因此会提醒10

    现在我希望你能理解输出。


编写此函数时:

1
2
3
4
5
function bar(){
    foo = 10;      
    return;
    function foo(){}
}

javascript读到:

1
2
3
4
5
function bar(){
    function foo(){}
    foo = 10;      
    return;
}

函数foo创建到本地函数bar中。当您编写foo = 10时,您覆盖了本地范围内的函数foo而不是全局变量。

所以你的警报给你1,因为你从不更新全局变量。


I know after return statement ,function foo() will not execute.

那不是真的。

函数声明被提升。

function foo(){}创建一个名为foo的局部变量(将新函数赋给它),然后foo = 10覆盖它。不过,您永远不会测试该foo变量的值。

In bar function , If I will remove function foo(). then alert(foo) will give me 10

您不再具有名为foo的局部变量,因此您将用相同的名称覆盖全局变量。

比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(function() {
  console.log("Version 1");
  var foo = 1;

  function bar() {
    console.log("At top of bar, foo is" + foo);
    foo = 10;
    console.log("After assignment in bar, foo is" + foo);
    return;

    function foo() {}
  }
  bar();
  console.log("Global foo is" + foo);
}());

(function() {
  console.log("Version 2");
  var foo = 1;

  function bar() {
    console.log("At top of bar, foo is" + foo);
    foo = 10;
    console.log("After assignment in bar, foo is" + foo);
    return;
  }
  bar();
  console.log("Global foo is" + foo);
}());


这里的问题是吊装和关闭。

声明function foo(){}被挂起,这意味着在这种情况下,即使它写在函数的末尾,它在范围内的任何地方都可以使用,包括在定义之前。

如果function foo(){}不存在,则说明foo = 10;覆盖了在全球范围内定义的foo。因此,全球foo === 10

如果存在function foo(){},说明foo = 10;只是覆盖了foo在局部范围内的功能,全局foo不会被触及,因此全局foo === 1

1
2
3
4
5
6
7
8
var foo = 1;
function bar(){
   console.log(typeof foo) // function      
   return;
   function foo() {}
}
bar();
alert(foo);

反对:

1
2
3
4
5
6
7
8
var foo = 1;
function bar(){
   console.log(typeof foo) // number      
   return;
   // function foo() {}
}
bar();
alert(foo);

所以基本上发生的事情就好像你已经宣布了var foo = 10。因为javascript中的函数声明被提升到了顶部编译器看到您的代码如下。

1
2
3
4
5
6
7
8
9
var foo = 1;
function bar(){
  var foo;
  foo = 10;      
  return;
  function foo(){}
}
bar();
alert(foo);

因此,事实上,foo = 10从未覆盖全球foo;它保持在功能的本地。所以警报将通过全球警报。


In addition to my previous answer in the same thread I am
adding another answer to put in more details about the Hoisting
feature in JavaScript as the previous answer is already accepted by the OP for its content.

首先,让我们熟悉范围是什么

范围在javascript中

Javascript初学者最困惑的一个原因是范围界定。实际上,这不仅仅是初学者。我见过很多经验丰富的javascript程序员,他们不完全理解范围。Javascript中范围界定如此混乱的原因是它看起来像C族语言。考虑以下C程序:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main() {
    int x = 1;
    printf("%d,", x); // 1
    if (1) {
        int x = 2;
        printf("%d,", x); // 2
    }
    printf("%d
"
, x); // 1
}

这个程序的输出是1,2,1。这是因为C和C族的其余部分具有块级范围。当控件进入一个块(如if语句)时,可以在该范围内声明新变量,而不影响外部范围。在JavaScript中不是这样。在Firebug中尝试以下操作:

1
2
3
4
5
6
7
var x = 1;
console.log(x); // 1
if (true) {
    var x = 2;
    console.log(x); // 2
}
console.log(x); // 2

在这种情况下,Firebug将显示1、2、2。这是因为javascript具有函数级作用域。这与C族截然不同。块(如if语句)不会创建新的作用域。只有函数才能创建新的作用域。

对于许多习惯于C语言、C++语言、C语言或Java语言的程序员来说,这是意外的和不受欢迎的。幸运的是,由于javascript函数的灵活性,这里有一个解决方案。如果必须在函数内创建临时作用域,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
function foo() {
    var x = 1;
    if (x) {
        (function () {
            var x = 2;
            // some other code
        }());
    }
    // x is still 1.
}

这个方法实际上非常灵活,可以在任何需要临时作用域的地方使用,而不仅仅是在block语句中。但是,我强烈建议您花时间真正理解和欣赏JavaScript范围。它非常强大,是我最喜欢的语言特征之一。如果你理解范围界定,提升对你来说会更有意义。

声明、姓名和起重

在javascript中,名称以四种基本方式之一进入作用域:

Language-defined:默认情况下,所有作用域都给出了名称this和参数。

Formal parameters:函数可以有命名的形式参数,这些参数的作用域是该函数的主体。

  • 函数声明:它们的形式为function foo() {}
  • 变量声明:采用var foo;的形式。
  • 函数声明和变量声明总是被javascript解释器无形地移动("提升")到其包含范围的顶部。
  • 显然,函数参数和语言定义的名称已经存在。这意味着这样的代码:

前任:

1
2
3
4
function foo() {
        bar();
        var x = 1;
    }

实际解释如下:

1
2
3
4
5
function foo() {
    var x;
    bar();
    x = 1;
}

结果表明,包含声明的行是否会被执行并不重要。以下两个功能是等效的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo() {
    if (false) {
        var x = 1;
    }
    return;
    var y = 1;
}

function foo() {
    var x, y;
    if (false) {
        x = 1;
    }
    return;
    y = 1;
}

注意声明的分配部分没有被提升。只有名字被提升。这与函数声明不同,整个函数体也将被提升。但是请记住,有两种正常的方法来声明函数。考虑以下javascript:

1
2
3
4
5
6
7
8
9
10
11
function test() {
    foo(); // TypeError"foo is not a function"
    bar(); //"this will run!"
    var foo = function () { // function expression assigned to local variable 'foo'
        alert("this won't run!");
    }
    function bar() { // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

在这种情况下,只有函数声明的主体被提升到顶部。"foo"这个名字被提升了,但是尸体被留在后面,在执行过程中被分配。

这包括了提升的基础。这个答案的100%全部归功于本·切丽。我不想在我的答案中发布这个链接,因为链接可能会断开,我发现这完全是信息性的,对于任何一个JavaScript开发人员都是必须阅读的。