Javascript函数无处不在,功能强大!Javascript函数可以让JS具有一些面向对象的特性,实现封装、继承等,也可以让代码得到复用。但是任何事情都有两个方面。Javascript函数有时是“任性的”。如果你不了解它的“气质”,它很可能会给你制造一些意想不到的麻烦(bug)。JavascriptFunction有两种类型:1)函数声明(FunctionDeclaration);//函数声明functionfunDeclaration(type){returntype==="Declaration";}2)函数表达式(FunctionExpression)。//函数表达式varfunExpression=function(type){returntype==="Expression";}上面的代码看起来很像,感觉上没有太大区别。但实际上,Javascript函数中的一个“陷阱”体现在Javascript中的两类函数定义上。我们看两段代码(分别标记为代码1和代码2):funDeclaration("Declaration");//=>truefunctionfunDeclaration(type){returntype==="Declaration";}funExpression("Expression");//=>errorvarfunExpression=function(type){returntype==="Expression";}用函数声明创建的函数funDeclaration可以在funDeclaration定义之前调用;而使用函数表达式创建的funExpression函数在调用之前不能在funExpression中赋值。为什么会这样?!有必要了解一下这两种JavascriptFunction的区别:使用函数声明创建的函数可以在函数解析后调用(比如解析时的逻辑处理);用函数表达式创建的函数在运行时进行赋值,直到表达式赋值完成后才能调用。这种差异可能看起来很小,但在某些情况下可能是一个难以发现的陷阱。造成这个陷阱的本质原因体现在Javascript函数提升(functionhoisting)和运行时(analysistime/runtime)这两种类型的区别上。以上两段代码的功能改进可以如下图所示:代码1的JS函数等价于:functionfunDeclaration(type){returntype==="Declaration";}funDeclaration("Declaration");//=>truecode2JS函数等价于:varfunExpression;funExpression("Expression");//==>errorfunExpression=function(type){returntype==="Expression";}上面代码运行时,只定义了funExpression变量,没有定义值。所以函数调用不能在undefined上进行。此时funExpression赋值语句还没有执行。为了进一步加深这两类JS函数的区别,下面给出一个比较容易混淆的例子,请看下面代码(代码段4):varsayHello;console.log(typeof(sayHey));//=>functionconsole.log(typeof(sayHo));//=>undefinedif(true){functionsayHey(){console.log("sayHey");}sayHello=functionsayHo(){console.log("sayHello");}}else{functionsayHey(){console.log("sayHey2");}sayHello=functionsayHo(){console.log("sayHello2");}}sayHey();//=>sayHey2sayHello();//=>sayHello分析:sayHey是用函数声明创建的。在解析JS时,JS编译器升级了函数定义。也就是说,在解析JS代码时,JS编译器(条件判断不形成新的作用域,两个sayHey函数定义都被条件判断解除)检测到的作用域中有两个同名的sayHey定义,第一个定义先被提升,然后第二个定义被提升(第二个定义在***定义中),第二个定义覆盖了第一个sayHey定义,所以sayHey()输出sayHey2;而sayHello是用函数表达式创建的,表达式的内容是JS运行时(不是解析时)才可以判断的(条件判断在这里起作用),所以sayHello表达式执行的是第一个函数定义并分配一个值,然后sayHello()输出sayHello。片段4中的代码实际上等同于以下代码(片段5):varsayHello;functionsayHey(){console.log("sayHey");}functionsayHey(){console.log("sayHey2");}log(typeof(sayHey));//=>functionconsole.log(typeof(sayHo));//=>undefinedif(true){//提升...sayHello=functionsayHo(){console.log("sayHello");}}else{//提升...sayHello=functionsayHo(){console.log("sayHello2");}}sayHey();//=>sayHey2sayHello();//=>sayHellosomepeoplemaybeDo你怀疑函数sayHey的定义是覆盖第一个的第二个?我们可以输出sayHey的源码,有图有真相,如下图:作用域,无论函数声明定义在哪里,都可以调用该函数。函数表达式的值在JS运行时就确定了,只有表达式赋值完成后才能调用函数。这种微小的差异可能会导致JS代码出现意想不到的错误,让你陷入莫名其妙的陷阱。***附上代码段4中sayHello和sayHey这两个函数的核心步骤(个人理解,如有异议请留言讨论):上图是执行的主要步骤示意图说你好功能。上图是sayHey函数执行的主要步骤示意图。【本文为专栏作家“谢军”原创稿件。转载可通过作者微信取得联系公众号(jingfeng18)】点此查看该作者更多好文
