Javascript 简明教程

JavaScript - Function Hoisting

Function Hoisting

在 JavaScript 中的 function hoisting 是默认行为,在此行为中, function declarations 在执行代码之前移至其局部作用域的顶部。因此,您可以在函数声明之前在其作用域中调用它。需要注意的是,仅提升声明,而不提升初始化。因此,变量或函数在使用之前应已初始化。

变量提升与函数提升类似,变量提升也是一种默认行为,在此行为中,变量声明移至其局部作用域的顶部。我们可以在函数声明之前使用此函数。

让我们看以下 JavaScript 代码。

add(5,10); // 15
function add(x, y){
   return x + y;
}

在上面的 JavaScript 代码中,函数 add 在其声明之前被调用。这是可能的,因为 JavaScript 解释器将函数声明提升到作用域的顶部。因此,上面的代码等效于 –

function add(x, y){
   return x + y;
}
add(5,10); // 15

函数提升仅适用于 function declaration ,而不适用于 function expression 。因此,如果使用函数表达式定义函数,则不会将其提升到顶部。

add(5,10); // ReferenceError: Cannot access 'add' before initialization
const add = function (x, y){
   return x + y;
}

让我们编写一些函数提升的完整 JavaScript 示例。

Example: Function Hoisting

在下面的示例中,我们定义了 printMessage() 函数,它会打印调用位置。

此外,我们在函数定义之前和之后调用了 printMessage() 函数。它在没有任何错误的情况下打印输出,因为函数提升到其作用域的顶部。

<html>
<body>
   <p id = "output"> </p>
   <script>
      printMessage("Top");
      function printMessage(pos) {
         document.getElementById("output").innerHTML +=
         "The function is called from the " + pos + "<br/>";
      }
      printMessage("Bottom");
   </script>
</body>
</html>
The function is called from the Top
The function is called from the Bottom

Example

在下面的示例中,我们在“if”块中定义了函数。因此,函数被提升到“if”块的顶部,您只能在初始化之前在“if”块中执行函数。

您不能在“if”块外部访问该函数。

<html>
<body>
   <p id = "output"> </p>
   <script>
      // test("Top"); // Out of scope
      if (1) {
         test("Top"); // In the same scope
         function test(pos) {
            document.getElementById("output").innerHTML +=
            "The function is called from the " + pos + "<br/>";
         }
         test("Bottom"); // In the same scope
      }
   </script>
</body>
</html>
The function is called from the Top
The function is called from the Bottom

提升是 JavaScript 中非常重要的行为。但始终建议在代码开头声明函数或变量。由于 JavaScript 始终按声明顺序、然后按初始化顺序,最后按使用顺序解释代码。

JavaScript Variable Hoisting

JavaScript 的提升行为默认将变量声明移至变量作用域的顶部。在 JavaScript 中,使用“var”关键字声明的变量会被提升到其作用域的顶部,但使用“let”和“const”关键字声明的变量不会被提升到顶部。

例如,

var x = y;
var y;

上面的代码与下面的代码类似,因为变量声明被提升到顶部。

var y;
var x = y;

让我们通过以下示例来了解变量提升。

Example: Variable Hoisting

在下面的示例中,我们初始化了变量 y,在输出中打印了它的值,并在最后声明了它。由于在全局作用域的顶部提升了变量 y,因此下面的代码会打印一个值,而不会出现任何错误。

<html>
<head>
   <title> JavaScript - Variable Hoisting </title>
</head>
<body>
   <p id = "output"> </p>
   <script>
      y = 10;
      document.getElementById("output").innerHTML =
	  "The value of the y is : " + y;
      var y;
   </script>
</body>
</html>
The value of the y is : 10

Example: Variable Hoisting with Function

在下面的示例中,我们定义了 printNum() 函数。在 printNum() 函数中,我们初始化了变量 y,打印了它的值,然后对其进行了声明。

变量 y 在函数顶部被提升,以便你可以在它的声明之前访问它,但你不能在函数外部访问它。

<html>
<head>
   <title> JavaScript - Variable Hoisting with function </title>
</head>
<body>
   <p id = "output"> </p>
   <script>
      const output = document.getElementById("output");
      function printNum() {
         y = 20;
         output.innerHTML += "The value of the variable y is : " + y;
         var y; // Function scope
      }
      printNum();
      // Variable Y can't be accessible here
   </script>
</body>
</html>
The value of the variable y is : 20

然而,变量初始化没有被提升到块的顶部。

Example: variable initialization is not hoisted

下面的示例演示了变量被提升到顶部,但变量初始化没有。在这里,我们打印变量 x,而没有初始化它。因此,它在输出中打印出“未定义”。

<html>
<body>
   <p id = "output"> </p>
   <script>
      var x;
      document.getElementById("output").innerHTML = "x is -> " + x;
      x = 10;
   </script>
</body>
</html>
x is -> undefined

Example: Hoisting with let and const keywords

下面的示例演示了使用“let”和“const”关键字声明的变量不被提升到顶部。因此,你总需要在使用之前定义它。

我们使用“try…​catch”块来处理错误。在输出中,用户可以观察到当我们访问用“let”关键字声明的变量时,下面的代码给出的错误。

<html>
<head>
   <title> Hoisting with let and const </title>
</head>
<body>
   <p id = "output"> </p>
   <script>
      const output = document.getElementById("output");
      try {
         x = 10;
         output.innerHTML += "The value of x is -> " + x;
         let x;
      }catch (error) {
         output.innerHTML += error.message;
      }
   </script>
</body>
</html>
Cannot access 'x' before initialization