前言JS基础知识对我们前端开发的重要性不言而喻~所以,我们都选择看一些书来充实自己.看完那些理论基础,你是一头雾水还是豁然开朗?透过现象看本质!我认为:当你有了理论基础作为基础的时候,你应该多思考一些代码的结果来实践这些理论。就像解决bug一样,首先要知道bug产生的原因,然后根据原因解决问题。代码案例varb=10;(functionb(){b=20;console.log(b);})();这段代码会输出什么?(ps:先别急着回答,自己想想)这段代码乍一看很简单,涉及的内容就是varfunctionIIFE,好像没什么问题。这段代码在严格模式下的输出:TypeError:Assignmenttoconstantvariable意思是:typeerror:assignmenttoconstantvariablesandoutputinnon-strictmode:outputanalysisandanalysisoutputinstrictmode如果这个TypeError:Assignmenttoconstantvariable.如果你做一个分析,那就说明变量b是不能修改的!现在的问题是,变量b指的是外部用var声明的变量b,还是立即执行的命名函数b?如果立即执行b命名的函数名,说实话我不确定它是否是可修改的。(ps:大部分js书籍并没有明确指出立即函数表达式是否可以重新赋值)但是我可以肯定,如果b引用了带有var的外部声明,那么它在这段代码中一定是可以修改的。我们都知道用var声明的全局变量在任何地方都必须是可修改的,因为变量在作用域的顶部。所以这里我想做一个大胆的假设:变量b指的是立即执行的具名函数b的名字~做出这个假设之后我想说:那是不是意味着在立即执行的函数中不能访问全局变量b命名函数b?其实不是,全局变量b对于立即执行的具名函数b是可以访问的,但是由于在具名函数b的内部作用域中还有一个用function声明的变量b,所以当代码执行时,js引擎首先发现用函数声明的变量b。书中提到,范围查询是通过由内向外的查询。(ps:我也手写过scope系列的文章:这就是我对JavaScript中scope的理解,希望对你有所帮助~)当然,在非严格模式下,你可以尝试使用immediateexecutednamedfunction内部函数打印window.b,这也可以证明在立即执行的namedfunctionb中可以访问到全局变量b!代码如下:varb=10;(functionb(){b=20;console.log(window.b);})();至此,我已经明确了立即执行函数b的内部作用域机制,我的问题又来了:为什么不能修改像(functionb(){}())这样的函数表达式呢?后来查了资料,明白了IIFE函数的内部机制~我的理解是:遇到命名函数表达式时,会创建一个辅助的具体对象,函数表达式的名字作为唯一key,用来存放函数表达式的名字,然后加入到函数的作用域链中,该值是只读的,不能删除,所以不能对该值进行操作。因此,在严格模式下,修改不可修改的常量后会报TypeError:Assignmenttoconstantvariable。解析非严格模式的输出在非严格模式下静默失败,因此不会报告任何错误。经过上面的分析,会输出立即执行的命名函数b本身。代码扩展我把代码重写为:varb=10;(functionb(){return1;})();console.log(b);这一段会输出什么?不管是非严格模式还是严格模式,这段代码都会输出10,也就是全局变量b的值~写这段代码的初衷不是猜结果,我想表达的是:why命名函数b的立即执行是否无法从外部访问?是否所有表达式都无法从外部访问?为了解决我的疑惑,我通过函数foo1和foo2分析如下:///fragment1varfoo1=function(){};console.log(foo1);//fragment2(functionfoo2(){})console.log(foo2);片段1是将一个匿名函数表达式赋值给变量foo1,然后可以用名字foo1——foo1()访问该函数。所以print是一个函数。片段2是一个函数表达式,但结果是UncaughtReferenceError:foo2isnotdefined。说明不可从外部访问。可以看出,命名函数b的立即执行是一个函数表达式,外部是访问不到的!(ps:意思是函数表达式既不能在函数声明前按名称调用,也不能在声明后调用)。其实大部分书上介绍的像Fragment1这样的代码,我之前理解的都是函数表达式。但是现在我的理解不是这样的~foo1是一个变量,匿名函数表达式赋值给了变量foo1,所以foo1是可以访问的!自己写了2个代码片段,解开了我的疑惑~思考其他Case业余时间修改了最原始的代码块,用这些代码来回顾和思考之前学习的理论基础。对于下面的代码片段,我会和大家一起公布结果,但我也会和大家分享我的经验~///片段1varb=10;functionb(){console.log(12);return1;}console.log(b,b());//10TypeError:为什么bisnotafunction打印10而不是函数b?论证:函数声明优先于变量声明~所以,相当于先用function声明函数b,然后用var覆盖b。//片段2console.log(b,b());//121varb=10;functionb(){console.log(12);return1;}为什么打印函数b、12和1的执行结果,反而全局变量b呢?参数:函数声明优先于变量声明~并且在这个过程中有变量提升。片段3varb=10;b=function(){b=20;console.log(b);//20return1;};console.log(b,b());//b函数,为什么1打印10,而不是函数b?论证:这个过程是对变量b重新赋值。因此,打印的是10,而不是函数。Fragment4varb=10;//Duplicateddeclaration"b"letb=function(){b=20;console.log(b);return1;};参数:用var声明的变量可以重复声明,用let声明的变量不能重复声明。所以,js引擎执行完varb=10语句后,还没遇到letb就报错了。综上所述,在写业务代码的时候,尽量避免声明变量命名冲突,因为上面几种情况的写法不合理,容易出现意想不到的结果。为什么不规范自己,避免不必要的麻烦呢?当然,我觉得在学习那些枯燥难懂的理论基础的时候,我觉得应该自己去创造自己的想象,多练习想想为什么会有这样的结果呢?根据理论确认结果!这会让你更好地理解理论基础,更好地运用它们,而不仅仅是死记硬背/死记硬背。原文:juejin.cn/user/2189882895108616本文转载自微信公众号“前端人”,可通过以下二维码关注。转载本文请联系前端达人公众号。
