JavaScript - 变量提升

7

这是一个简单的代码片段,但有些内容我不是很理解。

下面的代码会输出12,我理解这一点,因为var foo = 12;替换了之前对变量的声明。

<script>
var foo = 1;
function bar(){
  if (!foo) {
    var foo = 12;
  }
  alert(foo);
}
bar();
</script>

在下面的代码中,它会弹出警告1,这意味着在函数内部可以访问在函数外声明的变量。
  <script>
    var foo = 1;
    function bar(){
      alert(foo);
    }
    bar();
    </script>

但是,在下面的代码中,为什么会提示 undefined?我以为它将 alert 1,我只是将先前声明的变量分配给新变量。

  <script>
    var foo = 1;
    function bar(){
      if (!foo) {
        var foo = foo;
      }
      alert(foo);
    }
    bar();
    </script>

1
使用 var foo = window.foo; - Reeno
如果 (!foo),则检查全局变量 foo,但在 if 语句内部,您正在尝试使用另一个 foo 更改此全局变量的值,在 if 语句中未定义。 - Andrew Evt
5个回答

6

变量声明被推到函数的开头。

因此,实际上正在发生以下事情:

function bar(){
      var foo;
      if (!foo) {
        foo = foo;
      }
      alert(foo);
}

因此,您需要将其更改为使用window.foo,以便引用全局属性而不是函数的属性。
var foo = 1;
function bar(){
   var foo;
   if (!window.foo) {
      foo = window.foo;
   }
   alert(foo);
}
bar();

2
函数表达式也会发生同样的事情。 - maioman
明白了 :) 学到了新东西,谢谢 :) - Abhinav

4
提升(Hoisting)有一定的技巧性。函数声明与函数赋值一起被提升,但变量声明则是不带变量赋值的提升。因此,代码的执行顺序实际上是:
var foo;
var bar = function bar(){
  var foo; // undefined
  if (!foo) { // true
    foo = foo; // foo = undefined
  }
  alert(foo);
}
foo = 1;
bar();

如果您想引用全局变量foo,可以使用window.foo,或者更好的方法是使用不同的变量名:

var foo = 1;
function bar(){
  var baz =  foo;
  alert(baz);
}
bar();

3
下面的代码输出12,我理解这是因为var foo = 12;替换了之前的变量声明。
var foo = 1;
function bar(){
  if (!foo) {
    var foo = 12;
  }
  alert(foo);
}
bar();

你是正确的,因为局部变量会覆盖全局变量。

在下面的代码中,它会弹出1,这意味着在函数内声明的变量可以访问函数外部声明的变量。

var foo = 1;
function bar(){
  alert(foo);
}
bar();

你是正确的。变量foo在全局作用域中声明,因此可以从任何地方访问。

但是,在下面的代码中,为什么会提示undefined?我本以为它会提示1,因为我只是将先前声明的变量赋值给了新变量。

var foo = 1;
function bar(){
  if (!foo) {
    var foo = foo;
  }
  alert(foo);
}
bar();

这里有一些不同之处。您声明了一个全局变量和一个同名的局部变量。当JavaScript程序执行进入新函数时,函数中声明的所有变量都会被移动(或提升)到函数的顶部。

另一个例子:

var a = 123;

function f() {
    var a; // same as: var a = undefined;
    alert(a); // undefined
    a = 1;
    alert(a); // 1
}
f();


3
在JavaScript中,在ES5规范之前,作用域仅以函数体为基础实现。块级作用域的概念不存在(确实,将在下一个JavaScript版本中用let关键字实现)。
因此,如果你在函数体之外声明一个变量var something;,它将是全局的(在浏览器中,全局作用域是window对象的作用域)。
  1. 全局变量

var something = 'Hi Man';

/**
 * this is equal to:
**/

window.something = 'Hi Man';

如果您的代码在 严格模式 下无法运行,还有另一种声明全局变量的方法:省略 var 关键字。当省略 var 关键字时,变量属于(或移动到)全局作用域。
  1. 示例:

something = 'Hi Man';

/**
 * this is equal to:
**/

function someFunction() {
  something = 'Hi Man';
}

本地变量

由于不存在块级作用域,定义一个本地变量的唯一方式是在函数体中定义它。

  1. 示例

var something = 'Hi Man'; //global
console.log('globalVariable', something);

function someFunction() {
  var something = 'Hi Woman';
  console.log('localVariable', something);
  
  /**
   * defining variable that doesn't exists in global scope
  **/
  var localSomething = 'Hi People';
  console.log('another local variable', localSomething);
}

someFunction();
console.log('globalVariable after function execution', something);

try {
  console.log('try to access a local variable from global scope', localSomething);
} catch(e) { console.error(e); }

如您在此示例中所见,局部变量不存在于其作用域之外。这意味着另一件事情... 如果您使用var关键字在两个不同的作用域中声明相同的变量,则会得到两个不同的变量而不是在父作用域中定义的同一变量(名称)的覆盖。
如果您想在子作用域中“覆盖”相同的变量,则必须在没有var关键字的情况下使用它。由于作用域链,如果局部作用域中不存在变量,则将在其父作用域中搜索该变量。
  1. 示例

function someFunction() {
  something = 'Hi Woman';
}

var something = 'Hi Man';
console.log(1, 'something is', something);


someFunction();
console.log(1, 'something is', something);

最后一件事,变量提升。

如我下面所写的那样,目前还没有办法在代码的某个地方声明变量。它总是在其作用域的开头声明。

  1. 示例

function someFunction() {
  // doing something
  
  // doing something else
  
  var something = 'Hi Man';
}


/**
 * Probably you expect that the something variable will be defined after the 'doing 
 * something else' task, but, as javascript works, it will be defined on top of it scope.
 * So, the below snippet is equal to:
**/

function someFunction1() {
  var something;
  
  // doing something
  
  // doing something else
  
  something = 'Hi Man';
}

/**
 * You can try these following examples: 
 *
 * In the someFunction2 we try to access on a non-defined variable and this throws an
 * error.
 *
 * In the someFunction3, instead, we don't get any error because the variable that we expect to define later will be hoisted and defined at the top, so, the log is a simple undefined log.
**/

function someFunction2() {
  console.log(something);
};

function someFunction3() {
  console.log('before declaration', something);
  
  var something = 'Hi Man';
  
  console.log('after declaration', something);
}

这是因为在JavaScript中,变量声明有两个不同的步骤:

  • 定义
  • 初始化

function3示例则变成了以下形式:

function3Explained() {
  var something; // define it as undefined, this is the same as doing var something = undefined;
  
  // doing something;
  // doing something else;
  
  something = 'Hi Man';
  
}


2

在我看来,这与函数声明和提升无关,使用var在函数内部声明变量会在函数的隔离作用域中创建一个变量,这就是为什么你会得到未定义。

 var foo = 1;
 function funcOne() {
     var foo = foo;
     alert('foo is ' + foo);
 };
 funcOne();

 var bau = 1;
 function funcTwo() {
     bau = bau;
     alert('bau is ' + bau);
 };
 funcTwo();

fiddle


这是一个不错的例子,谢谢 :) - Abhinav

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