34

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

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();

任何指针将不胜感激。

4

7 回答 7

35

引用MDN Docs 关于var吊装

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

因此,在您的情况下,JavaScript 知道在函数中的某处定义了一个局部变量(不是在外部声明的变量) x,但它不知道它的实际值,直到执行到达分配给x. (声明在编译时处理,分配在执行时完成)直到分配完成,undefined将使用默认值。既然undefined是假的,条件

if (!x) {

满足并执行赋值语句。这就是您进入hoisted警报框的原因。


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

var x;

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

y();
alert(x);

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

于 2015-04-16T06:52:46.650 回答
12

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

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

y();

y执行时,会var x遮蔽外部范围x,因此y函数内部x位于undefined声明的第一行之后。

于 2015-04-16T06:51:59.057 回答
2

这个答案非常详细地解释了变量的不同范围。在您的特定情况下,您可以使用以下参考:

this.x; 

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

和部分

if(!x)

这是真的,因为您正在测试:is falsex这一点上是undefined因为在函数范围内不存在,并且undefined被认为是 JS 中的错误值之一,有关更多信息,请查看此处

于 2015-04-16T06:58:44.687 回答
0

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

   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();
于 2020-07-21T07:47:17.660 回答
0

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

于 2021-01-07T11:02:41.977 回答
0

在 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);
    }
于 2019-12-16T13:16:41.693 回答
-1

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

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

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

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

起初我没有看到我的做法有问题,直到我开始使用更多功能,然后这些提升问题开始出现,我不明白为什么,我只是假设我不能使用相同的变量跨函数命名,或者我无法引用同一个函数之外的变量,或者我不得不使用一些奇怪的方法(如this.variablewindow.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();

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

于 2021-03-31T21:07:16.520 回答