JavaScript调用提升函数时use strict未生效的原因
这个现象的本质是你写的"use strict"根本没有生效,整个脚本全程运行在非严格模式(俗称"马虎模式/sloppy mode")下,和函数声明提升没有直接关系。
严格模式指令的生效规则
"use strict"从设计上就不是一个全局开关语句,而是一类特殊的「指令序言(Directive Prologue)」,ECMAScript规范对它的生效位置有非常严格的要求:
- 它必须是所在作用域(脚本全局作用域/函数作用域)源码静态位置的最开头部分,前面只能有其他字符串形式的指令、或者代码注释,不能出现任何其他类型的实际语句。
- 只要在
"use strict"的源码前面出现了函数调用、变量声明、普通字面量表达式这类非指令语句,JS引擎在代码解析阶段就不会把它识别为严格模式开关,只会把它当成一个没有任何作用的普通字符串,直接忽略。
你写的第二份代码里,"use strict";的源码前一行就是myFunction();这个函数调用语句,从静态位置上就不符合指令序言的要求,所以从解析阶段开始,整个脚本就没有进入严格模式,严格模式的所有规则自然都不会生效。
注意:指令序言的判定是解析阶段基于源码的静态文本位置做的,和执行阶段的声明提升没有任何关系——哪怕函数声明会被提升到作用域顶部,只要源码里
"use strict"前面有非指令语句,它就不会触发严格模式。
为什么没触发未声明变量报错
因为非严格模式下,JS对未声明变量的赋值行为有特殊的兼容逻辑:当你直接写x = 12而没有用var/let/const声明x时,引擎会沿着作用域链向上查找x的声明,如果一直找到全局作用域都没找到,不会抛错,反而会自动在全局对象上创建一个名为x的属性,也就是大家常说的「隐式全局变量」。
这也是为什么你写的x=12没有触发ReferenceError,脚本还能顺利执行到最后打印the script continues...——这本来就是非严格模式的默认行为。
你可以做个简单验证:把第二份代码里的"use strict"挪到脚本第一行(前面只留注释也可以),哪怕保留函数提前调用的写法,执行到x=12时照样会抛出你预期的未定义错误:
"use strict"; myFunction(); x = 12; function myFunction() { } console.log('the script continues...') // 这行永远不会执行,因为前面已经抛错
为什么会影响全局作用域
就是上面提到的非严格模式隐式全局变量规则:你赋值的x没有任何块级/函数级的声明,最终会被直接挂载到全局对象(浏览器环境是window,Node.js环境是globalThis)上,成为全局可访问的属性。
这种隐式创建的全局变量很容易造成命名冲突、意外的值覆盖,也是严格模式专门要禁止这类写法的核心原因。
内容的提问来源于stack exchange,提问作者someusername12

