运行以下代码:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
main()
class Foo { }
function main() { Foo() }
</script>
</body>
</html>
生成ReferenceError:在初始化之前无法访问词法声明“Foo”
(无论如何,在 Firefox 91.5.1esr 上)。
一个简单的解决方法是将类定义向上移动一行。将 class Foo
替换为 function Foo()
也将消除错误消息。将 Foo()
更改为 new Foo()
,没有帮助:错误仍然存在。
问题:是否有引用标准和/或清晰的描述来解释此行为?
最佳答案
一个类
是一个ClassDeclaration
在规范中。这被分类为 DeclarationPart
,出于 LexicallyScopedDeclarations 的目的,它被分类为 StatementListItem .
当 block 或函数最初被评估时(在 block 中的代码实际开始运行之前),它将执行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).
这些LexicallyScopedDeclarations
是使用ES6+语法创建的标识符,并且不包括var
。 (使用 var
创建的标识符被分类为 varNames
或 VarDeclaredNames
,它们经历的过程与上面的步骤 33-34 不同。)
因此,在 block 或函数的开头,class
标识符(以及 const
和 let
标识符)都具有已被绑定(bind)的绑定(bind)在环境中创建,但尚未在环境中初始化。当 BindingClassDeclarationEvaluation
时发生初始化运行,它会:
5. Perform ? InitializeBoundName(className, value, env).
只有当引擎实际运行 block 或函数内的代码并遇到class SomeClassName
时,才会发生这种情况。 (这与前面引用的过程是分开的,即引擎仅查看 block 的文本以获取内部声明的标识符列表。)
当一个 block 正在运行时,您尝试使用new
实例化某些东西,EvaluateNew
运行,其中包括:
Let constructor be ? GetValue(ref).
最终运行 GetBindingValue
,其中有:
2. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
只有当 InitializeBoundName
运行时,即引擎遇到类声明时,绑定(bind)才会被初始化。
关于javascript - 引用错误: can't access lexical declaration 'Foo' before initialization,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72081570/