最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReferenceError: can't access lexical declaration 'Foo' before initialization - Stack Overfl

programmeradmin3浏览0评论

Running the following code:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
<script>
  main()
  class Foo { }
  function main() { Foo() }
</script>
</body>
</html>

generates ReferenceError: can't access lexical declaration 'Foo' before initialization (on Firefox 91.5.1esr, anyway).

A simple fix is to move the class definition up a line. Replacing class Foo with function Foo() will also eliminate the error message. Changing Foo() to new Foo(), does not help: the error remains.

Question: Is there a reference to the standards, and/or a clear description, explaining this behavior?

Running the following code:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
<script>
  main()
  class Foo { }
  function main() { Foo() }
</script>
</body>
</html>

generates ReferenceError: can't access lexical declaration 'Foo' before initialization (on Firefox 91.5.1esr, anyway).

A simple fix is to move the class definition up a line. Replacing class Foo with function Foo() will also eliminate the error message. Changing Foo() to new Foo(), does not help: the error remains.

Question: Is there a reference to the standards, and/or a clear description, explaining this behavior?

Share Improve this question edited May 2, 2022 at 12:38 Joseph Quinsey asked May 2, 2022 at 1:25 Joseph QuinseyJoseph Quinsey 9,97210 gold badges57 silver badges80 bronze badges 1
  • 1 @CertainPerformance: I don't entirely agree that this is a duplicate. However, T.J. Crowder's ment there is interesting: "Hoisting has been an almost endless source of misunderstanding and confusion. All of the new declaration constructs (let, const, class) added in ES6 are un-hoisted (well, they're half-hoisted). Barring a quote from Eich or similar, you're not going to get an answer that isn't effectively speculation." – Joseph Quinsey Commented May 2, 2022 at 1:54
Add a ment  | 

2 Answers 2

Reset to default 3

A class is a ClassDeclaration in the spec. This is classified as a DeclarationPart, which is classified as a StatementListItem for the purposes of LexicallyScopedDeclarations.

When a block or function is evaluated initially (before the code in the block actually starts running), it will do something like:

33. Let lexDeclarations be the LexicallyScopedDeclarations of code.
34. For each element d of lexDeclarations, do
  a. NOTE: A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized.
  b. For each element dn of the BoundNames of d, do
    i. If IsConstantDeclaration of d is true, then
      1. Perform ! lexEnv.CreateImmutableBinding(dn, true).
    ii. Else,
      1. Perform ! lexEnv.CreateMutableBinding(dn, false).

These LexicallyScopedDeclarations are identifiers created with ES6+ syntax, and exclude var. (Identifiers created with var are classified as varNames, or VarDeclaredNames, which go through a different process than steps 33-34 above.)

So, at the beginning of a block or function, class identifiers (and const and let identifiers) all have bindings that have been created in the environment, but have not been initialized in the environment. Initialization occurs when BindingClassDeclarationEvaluation runs, which does:

5. Perform ? InitializeBoundName(className, value, env).

This only happens when the engine is actually running the code inside the block or function and es across class SomeClassName. (This is separate from the process referenced earlier, which is when the engine is only looking through the text of the block to get a list of identifiers that are declared inside.)


When a block is running, and you try to instantiate something with new, EvaluateNew runs, which does, among other things:

Let constructor be ? GetValue(ref).

which eventually runs GetBindingValue, which has:

2. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.

The binding only gets initialized when InitializeBoundName runs - that is, when the engine es across the class declaration.

A simple way to understand why this error happens involves understanding hoisting. Before execution, an interpreter (like a browser engine) will move all variables and functions to the top of the file, likely for ease of access. But more modern instances of variables and functions, such as classes and function expressions, might get hoisted to the top but without being initialized. This is why that although the class is found, it has no proper value assigned to it, even if empty, thus an error occurs.

发布评论

评论列表(0)

  1. 暂无评论