JavaScript中类实例化顺序为何会触发ReferenceError报错?
问题核心原因
1. 你的传参写法本身存在语法误用
你写的new B(host = DEFAULT_HOST)不是JS中给函数传递参数的正确写法,这段代码的实际执行逻辑是:先执行host = DEFAULT_HOST这个赋值表达式,再把表达式的返回值(也就是DEFAULT_HOST的值)作为第一个参数传给B的构造函数。你以为这是指定参数名传参,但JS原生不支持命名参数传参,你这里本质是做了一次变量赋值操作。
2. Class默认运行在严格模式
ES6的Class内部所有代码默认运行在严格模式下,严格模式的核心规则之一就是不允许给未声明的变量直接赋值,一旦出现就会抛出ReferenceError。
你在A的constructor里写host = DEFAULT_HOST的时候,当前作用域(包括上层全局作用域)都没有声明过host变量,所以严格模式下直接触发报错。
3. 调换执行顺序不报错的原因
你把console.log(new B(host=123));放到前面执行的时候,这行代码是在全局作用域的非严格模式下运行的。非严格模式下给未声明的变量赋值,JS会自动在全局作用域创建一个名为host的全局变量,不会报错。
等后面执行A的constructor里的host = DEFAULT_HOST的时候,全局已经存在host变量了,自然不会再抛出未定义的错误。
4. 普通函数版本正常的原因
普通函数默认运行在非严格模式下,你在a函数里执行host = DEFAULT_HOST的时候,非严格模式自动创建全局host变量,所以不会触发报错。
修复方案
把错误的参数写法改成正常的传参即可,不需要写参数名:
const DEFAULT_HOST = "localhost"; class B { constructor(host = null) { console.log(`HOST: ${host}`); } } class A { constructor() { new B(DEFAULT_HOST) } } console.log(new A()); console.log(new B(123));
这样不管执行顺序是什么,都不会出现报错,也不会意外创建全局变量。
内容的提问来源于stack exchange,提问作者user48956

