【问题标题】:'Hoisted' JavaScript Variables“提升” JavaScript 变量
【发布时间】:2015-06-22 09:58:43
【问题描述】:

我不完全明白为什么以下显示“提升”到最后。

var x = 'set';
var y = function () 
{
    // WHAT YOU DON'T SEE -> var x; 
    // is effectively "hoisted" to this line!

    if (!x) 
    { 
        // You might expect the variable to be populated at this point...it is not
        // though, so this block executes
        var x = 'hoisted'; 
    }

    alert(x); 
}

//... and this call causes an alert to display "hoisted"
y();

任何指针将不胜感激。

【问题讨论】:

  • 它等同于var y = function () { var x; ...,这使得x 成为函数的本地并且未分配。如果块不会“停止”var 提升
  • 是的,var x 提升到作用域的顶部,这是 JS 中变量声明的正常行为。
  • @Qantas94Heavy:这个实际上可能成为变量提升的一个很好的规范副本,afaik 我们目前没有。仅适用于function hoisting...
  • @Bergi:是的,这似乎比我链接的问题更广泛。我会将另一个问题标记为与此问题重复。

标签: javascript hoisting


【解决方案1】:

引用MDN Docs on var hoisting

因为变量声明(以及一般的声明)是在任何代码执行之前处理的,所以在代码的任何地方声明一个变量就相当于在顶部声明它。这也意味着一个变量可以似乎在声明之前已使用。这种行为称为“提升”,因为变量声明似乎被移到函数或全局代码的顶部。

所以,在你的情况下,JavaScript 知道 一个局部变量(不是在外部声明的那个) x 定义在函数的某处,但它不知道它的实际值,直到执行到达分配给x 的赋值语句。 (声明在编译时处理,赋值在执行时完成) 直到赋值完成,将使用默认值undefined。由于undefined is falsy,条件

if (!x) {

满足并执行赋值语句。这就是为什么您会在警告框中收到hoisted


假设你没有在函数内部声明x

var x;

var y = function () {
    if (!x) {
        x = 'hoisted';
    }
    alert(x);
}

y();
alert(x);

这里,由于 x 没有在函数中的任何地方声明,因此在运行时,JavaScript 将在更高的范围内查找 x。在这种情况下,它会在函数之外找到它。所以,x 将被使用。由于您将hoisted 分配给x,因此内部的alert 也会说hoisted,并且在离开该功能后,alert(x) 也会提醒hoisted

【讨论】:

    【解决方案2】:

    变量声明提升到范围的顶部。所以你的代码相当于这个:

    var x = 'set';
    var y = function () {
        var x;
        if (!x) {
            x = 'hoisted';
        }
        alert(x);
    }
    
    y();
    

    y被执行时,var x遮蔽了外部作用域x所以在y函数内部x在声明的第一行之后是undefined

    【讨论】:

    • 函数中如何获取全局变量的值?
    • @mhkyazd 使用全局对象。
    • @mhkyazd 如果x 来自外部范围,则不要在函数的任何位置写入var x,因此它不会被覆盖。
    • @mhkyazd 如果你想在你的函数中使用全局变量,那么不要在里面重新声明x
    【解决方案3】:

    this answer 非常详细地解释了变量的不同作用域。在您的特定情况下,您可以使用以下引用:

    this.x; 
    

    访问函数外部的全局 x 变量。因为在函数内部您试图访问未定义的变量,所以使用this 关键字可以引用函数外部的变量。

    还有部分

    if(!x)
    

    这是真的,因为您正在测试:is falsex 在那一点上是 undefined 因为在函数范围内不存在,而 undefined 被认为是 JS 中的错误值之一,以获取更多信息please take a look here.

    【讨论】:

    • 请注意,使用this 仅在这种特定情况下有效,因为x 被直接调用——如果它被放到不同的对象上,那么this 将不再引用全局对象。
    【解决方案4】:

    在 javascript console.log 中,函数内部给出未定义的输出作为未传递参数的输出,但在函数外部它给出未定义错误,因为函数内部浏览器显式声明了变量。例如

    console.log(x) 
    

    给出 VM1533:1 Uncaught ReferenceError: x is not defined 而

    function test(x) {
    console.log(x)
    }
    test(); 
    

    给出未定义的。 这是因为函数test()被浏览器重写为:

    function test(x) {
        var x;
        console.log(x)
        }
    

    另一个例子:-

    var x =5 ;
    function test(x) {
    console.log(x)
    }
    test(); 
    

    随着函数的变化仍未定义

    function test(x) {
        var x;
        console.log(x)
        }
    

    以下示例中的警报将给出未定义的:-

        var x =5;
        function test() {
        alert(x);
        var x =10;
        }
    
    test(); 
    

    上面的函数会变成:-

     function test() {
        var x;
        alert(x);
        x =10;
        }
    

    函数内 javascript 变量的作用域是函数级作用域,而不是块级作用域。例如

    function varScope() {
    
    for(var i = 0; i < 10; i++){
        for(var j = 0; j < 5; j++){}
            console.log("j is "+j)
        }
        console.log("i is "+i);
    
    
    }
    varScope();
    

    将输出为:

    j is 5 
    i is 10
    

    函数又变成了:-

      function varScope() {
        var i;
        var j;
        for(i = 0; i < 10; i++){
            for(j = 0; j < 5; j++){}
                console.log("j is "+j)
            }
            console.log("i is "+i);
        }
    

    【讨论】:

      【解决方案5】:

      使用这个关键字有助于在被提升的函数之外引用一个变量。 你的情况

         this.x
      

      更新: 从 ES2015 开始,使用 const 和 let 关键字使变量不会被提升。

      示例:在函数中提升全局变量

       var x = "global variable hoisted";
      function check_hoisting(){
       alert(x); //undefined because of js hoisting
      var x = "local variable";
       }
       check_hoisting();
      

      示例:变量因 let 关键字而未提升

      let x = "global variable not hoisted";
      function check_hoisting(){
       alert(x); 
      var x = "local variable";
       
        }
          check_hoisting();
      

      【讨论】:

        【解决方案6】:

        JavaScript 仅提升声明,而不是初始化。

        【讨论】:

          【解决方案7】:

          作为一个新程序员,无论我在这个问题或类似问题中阅读了多少答案,我都无法弄清楚这个问题的解决方案。因此,我正在为像我这样通过绝望搜索到达这里的其他人分享我的解决方案(加上所有寻找修复的相关问题都被锁定并链接到这个问题)。因此,这是您问题的答案,像我这样的新手可以理解如何以及为什么更改我们的代码以避免此问题并使变量返回我们期望的值。我的问题是,在学习 javascript 时,我假设每次我想修改一个变量时,我都必须以 var 开头,例如:

          var myVariable = 1;
          ...
          var myVariable = myVariable + 3;
          

          实际上,您应该只在第一次输入var(当您声明/初始化变量时)。在此之后的所有其他时间,您必须以变量名开头并删除var,例如:

          var myVariable = 1;
          ...
          myVariable = myVariable + 3;
          

          一开始我并没有发现我是怎么做的,直到我开始使用更多的功能,然后这些提升问题开始出现,我不明白为什么,我只是假设我不能使用跨函数使用相同的变量名,或者我无法引用同一函数之外的变量,或者我不得不使用诸如 this.variable 之类的奇怪方法强制创建/使用全局变量>window.variable,或者其他奇怪的东西。

          在我删除了 var 除了每个变量的第一个位置之外的所有位置后,我的问题就消失了。

          同样的事情也适用于您的代码。如果你改变这个:

          var x = 'set';
          var y = function () 
          {
              if (!x) 
              { 
                  var x = 'hoisted'; 
              }
              alert(x); 
          }
          
          y();
          

          (将var x = 'hoisted'; 更改为x = 'hoisted';)为:

          var x = 'set';
          var y = function () 
          {
              if (!x) 
              { 
                  x = 'hoisted'; 
              }
              alert(x); 
          }
          
          y();
          

          然后它会按您的预期工作,并避免吊装。

          【讨论】:

          • 如果我的回答有问题,请评论它是什么,而不是仅仅投反对票。关于这个或任何其他答案(其中都链接到这里)的其他 cmet 都没有帮助我理解这一点。
          猜你喜欢
          • 1970-01-01
          • 2014-12-31
          • 1970-01-01
          • 1970-01-01
          • 2015-01-01
          • 2019-05-07
          • 2021-12-23
          • 2017-07-18
          相关资源
          最近更新 更多