JavaScript匿名函数内访问fn返回undefined的异常原因
问题根本原因
这个怪异现象没有任何玄学成分,完全是JavaScript的语法解析歧义 + 自动分号插入(ASI)规则共同导致的经典坑,和你定义的fn本身没有任何关系。
出错时的真实解析逻辑
你遇到输出undefined的这段代码:
var fn = function(div,speed){ } (function(){console.log(fn)}())
JS引擎不会把换行当成强制的语句结束标记,当它看到上一行以闭合大括号结尾、下一行直接以括号开头时,会自动把两段代码拼接成同一条语法语句,最终实际的解析效果等价于你手写出来的这段代码:
var fn = function(div, speed) {}(function(){console.log(fn)}());
对应的执行顺序就完全和你预期的不一样了:
- 引擎首先创建那个匿名空函数,紧接着立刻调用这个刚创建的空函数,调用时把后面括号包裹的整个IIFE当成传参内容
- 在执行传入的这个IIFE的瞬间,
fn的赋值操作根本还没走完——此时fn还处于var声明的变量提升阶段,值为undefined,所以控制台自然打出undefined - 等空函数调用完成(因为函数体没有return语句,调用结果是
undefined),这个返回值才会被真正赋值给fn变量
两种可正常运行的改法的原理
- 两段代码中间加一行
var weird = true;
这行独立的语句会直接打断前后代码的语法连续状态,引擎不会再把后面的IIFE和前面的fn函数表达式拼成同一条语句,等IIFE执行时fn的赋值已经彻底完成,自然可以正常打印出函数对象。 - 给IIFE前置逻辑非运算符
!
逻辑非运算符会明确标记当前是一个全新的表达式语句,从根源上避免引擎把后续的IIFE当成前面函数的调用参数,自然就能按预期逻辑执行,拿到已经赋值完成的fn。
通用避坑提示
这种语法歧义是JS写立即执行函数时的经典坑,常规的规避方式有两个:
- 每一条独立语句结尾主动写上分号,避免换行带来的拼接歧义
- 写IIFE时在表达式前加
;、!、+、~这类标记,强制把当前语句和上一行做语法切割,比如大家常见的写法;(function(){})(),最前面的分号就是用来兜底,防止和上一行没写结尾分号的代码粘成奇怪的语句。
内容的提问来源于stack exchange,提问作者CaptainMagmelaton Cap
相关产品推荐
相关产品推荐

