首页 > 解决方案 > 如何在 Javascript 中使用带函数表达式的变量提升?

问题描述

我知道变量提升是将声明移动到函数或全局上下文顶部的行为。这显示在下面的代码中:

console.log(a);
var a = 'Hello World!';

上面的代码实际上得到了以下行为:

var a;
console.log(a);
a = 'Hello World!';

上述代码的结果输出是undefined.

在上面的示例中,重要的是要注意变量提升仅移动顶部的声明部分。初始化部分没有移到顶部。

当我尝试使用函数表达式复制相同的行为时,我得到了一个意外的输出。

这显示在下面的代码中:

function sayHello() {
    var say = function() { console.log(hello); }
    var hello= 'Hello World !!';
    return say;
}
sayHello()(); // output Hello World !!

我试图理解为什么我们没有收到undefined输出,因为该变量hello仅在使用后才被初始化(类似于前面的示例)。

标签: javascriptfunctionhoisting

解决方案


您看到的行为来自闭包的工作方式。并不是说 的值以hello某种方式被提升,而是读取helloconsole.log(hello)部分)值的代码仅在您设置值后才执行!

为了不在这里与提升混淆,因为这与您所看到的并不真正相关,让我们以提升后的方式写出代码:

function sayHello() {
    var hello;
    var say = function() { console.log(hello); }
    hello = 'Hello World !!';
    return say;
}
sayHello()(); // output Hello World !!

如果您say()在该行之前插入 a hello = 'Hello World !!';,它将打印undefined.

现在,当你有这样的代码时......

    var hello;
    var say = function() { console.log(hello); }

...那么hello内部say不是外部的副本hello,它实际上是您在此处引用的同一个变量。并且即使在外部函数返回后它仍然存在,因为现在您拥有对该函数的引用,而该say函数又仍然保留对hello.

我试图理解为什么我们没有收到undefined输出,因为该变量hello 仅在使用后才被初始化(类似于前面的示例)。

我想这是你的误解。当您say在最后调用函数时使用它 - 按时间顺序。从上到下查看代码hello之前似乎被“使用”过的事实并不重要,因为这不是代码执行的顺序。在var say = function () { console.log(hello); }你刚刚做的那一行var say = something;something在这一点上没有任何意义(它只需要在语法上是正确的)。hello只有在您最后调用时才会访问say

(您也可以尝试使用类似的东西var say = function () { derp(); },并且您会注意到derp is not defined只有在调用该函数时才会收到错误,而不是在定义它时。)

所以,按时间顺序,这就是发生的事情:

  • sayHello叫做
  • hello被创建并包含undefined
  • say被创建并分配了一个带有 body 的函数表达式console.log(hello)
  • hello被安排了"Hello World !!"
  • sayHello返回say
  • 函数表达式(原为say)被调用
  • console.log(hello)执行
    • 请记住,此时hello包含"Hello World !!"
  • 字符串"Hello World !!"被打印出来

您可以扩展此示例,以帮助理解:

function sayHello() {
  var hello;
  var say = function() { console.log(hello); }  
  hello = 'Hello World !!';
  var think = function() { hello = 'What should I think about?'; }
  return [say, think];
}

var [say, think] = sayHello();
say(); // Prints "Hello World !!";
think();
say(); // prints "What should I think about?"

正如您所看到的,hello它们仍然存在并且可以被两者使用,say并且think即使在sayHello返回之后,代码的所有这些部分都引用同一个hello变量。


推荐阅读