为何创建数组并执行push操作的单行代码无法运行?及报错类型相关疑问
这是个非常典型的**自动分号插入(ASI)**陷阱问题,咱们一步步拆解来搞明白:
首先看能正常运行的多行代码:
var b = {} b['foo']=[] b['foo'].push(1)
这段代码能跑通,是因为JavaScript的自动分号插入机制会在每行末尾自动补充分号,相当于手动加了分号的效果,变量b先被赋值为空对象,再依次执行后面的属性赋值和push操作,逻辑完全通顺。
但改成单行写法后:
var b = {} (b['foo']=[]).push(1)
问题就来了——JavaScript的编译器不会自动在{}和后面的(之间插入分号!它会把这段代码解析成:
var b = {}( (b['foo']=[]).push(1) )
也就是把{}当成一个函数,试图调用它并传入(b['foo']=[]).push(1)作为参数。
为什么会报"Cannot set properties of undefined (setting 'foo')"?
这里的关键是变量初始化的时机:JavaScript中var声明的变量会提升,但赋值操作是在执行阶段按顺序进行的。在上面的解析结果里,整个表达式是var b = {}(...),右边的函数调用会优先执行,而此时b还没有被赋值为{}(因为赋值还没完成),所以b的当前值是undefined。当尝试执行b['foo']=[]时,自然就会抛出“无法给undefined设置属性‘foo’”的错误。
为什么不是报错"b不是函数"?
这是因为函数调用的参数会先被求值!编译器会先尝试计算(b['foo']=[]).push(1)这个参数,而第一步就是执行b['foo']=[],这时候b还是undefined,直接触发了这个错误,根本没走到判断{}是不是可调用函数的那一步。如果咱们把代码改成var b = 1 (b['foo']=[]).push(1),等b初始化完成后,才会报错“b不是函数”,但你的例子里错误提前触发了。
最后验证一下:加上分号后代码就正常了:
var b = {}; (b['foo']=[]).push(1)
手动加的分号让编译器把代码分成两个独立的语句,先完成b的赋值,再执行后面的push操作,逻辑就没问题了。
内容的提问来源于stack exchange,提问作者Daavee18

