当javaScript从入门到提高前需要注意的细节:函数部分

简介:

在javaScript中,函数是一个很基础的对象,同样也是非常随意,定义起来很随意,用起来那是更加随意。以下说明在javaScript中声明一个函数那是多麽的随意

 
  1. function fun1() {//声明一个函数  
  2.  
  3. }  
  4.  
  5. function() { //声明一个匿名函数  
  6.  
  7. }  
  8.  
  9. var fun2 = function() {//声明一个变量指向一个匿名的函数表达式  
  10.  
  11. }  
  12.  
  13. var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式  
  14.  
  15. }  
  16.  
  17. function fun5() {  
  18.     return function() { //返回一个匿名函数  
  19.  
  20.     }  

那function和var的区别有哪些?我们不讨论深奥的,我们看下哪些运行结果

 
  1. alert(typeof fun1); //function  
  2. alert(typeof fun2); //undefined  
  3. alert(typeof fun3); //undefined  
  4. alert(typeof fun4); //function  
  5. alert(typeof fun5); //function  
  6.  
  7.  
  8. function fun1() {//声明一个函数  
  9.  
  10. }  
  11.  
  12. function() { //声明一个匿名函数  
  13.  
  14. }  
  15.  
  16. var fun2 = function() {//声明一个变量指向一个匿名的函数表达式  
  17.  
  18. }  
  19.  
  20. var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式  
  21.  
  22. }  
  23.  
  24. function fun5() {  
  25.     return function() { //返回一个匿名函数  
  26.  
  27.     }  

我们可以看到function比var还要优先。
我们之前说变量的时候已经说过,一个变量可以被反复赋值,对于函数来说,这可以吗?
对于习惯在静态语言下写代码的人,看到如下代码估计要气愤了,这个是啥破代码?

 
  1. function fun1() {  
  2.     alert("Y");  
  3. }  
  4.  
  5. function fun1() {  
  6.     alert("N");  

但事实上,这个代码是可以执行的,执行的结果暗示我们函数的名称会被自动的作为对象的一个属性,函数体就是属性的值了

 
  1. fun1(); // n  
  2.  
  3. this["fun1"](); // n  
  4. this.fun1(); // n  
  5. window["fun1"](); // n  
  6. window.fun1(); // n 

现在我们把两个因素合起来看看,function在所有执行代码前先编译,且后面的function会覆盖前面的function定义,那么,如下的代码执行的是?

 
  1. var input = 5;  
  2. switch (input) {  
  3.     case 10:  
  4.         function fun1() {  
  5.             alert(10);  
  6.         }  
  7.         break;  
  8.     case 5:  
  9.         function fun1() {  
  10.             alert(5);  
  11.         }  
  12.         break;  
  13.     default:  
  14.         function fun1() {  
  15.             alert("default");  
  16.         }  
  17.         break;  

令人恼怒的是,在chrome和IE下执行的是我们前面的推断,function在所有执行代码前先编译,且后面的function会覆盖前面的function定义结果是

 
  1. fun1(); //default 

而FireFox把if中的语句作为执行快,运行时才编译,所以他的结果是

 
  1. fun1(); //5 

为了解决这个问题,你需要吧动态分配的函数以函数的表达式来执行,避免编译器对函数还没有执行到就已经分配了,比如

 
  1. var input = 5;  
  2. switch (input) {  
  3.     case 10:  
  4.         fun1 = function() {  
  5.             alert(10);  
  6.         }  
  7.         break;  
  8.     case 5:  
  9.         fun1 = function() {  
  10.             alert(5);  
  11.         }  
  12.         break;  
  13.     default:  
  14.         fun1 = function() {  
  15.             alert("default");  
  16.         }  
  17.         break;  

说到函数的表达式,那什么是函数的表达式呢?简单的说,在右边的都是函数的表达式

 
  1. var fun1 = function() {//函数表达式  
  2.  
  3. }  
  4.  
  5. var fun1 = function fun2() {//函数表达式  
  6.  
  7. }  
  8.  
  9. var obj = {  
  10.     Do: function() {//函数表达式  
  11.  
  12.     }  
  13. }  
  14.  
  15. new function() {//函数表达式  
  16.  
  17. }  
  18.  
  19. function fun3() {  
  20.     return function() { //函数表达式  
  21.  
  22.     }  

还有两种就是在()内和【】内,原谅我为了看的清楚了些,用了中文的【】,具体看下代码就ok了

 
  1. (function() { });  
  2. [function() { } ]; 

函数表达式和一般的表达式的值一样,都是执行到的时候才编译,不过有一个例外,就是非匿名的函数表达式

 
  1. var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式  

上面的代码如果你没有特定的含义,求你最好不要这么写,因为会出现你想不到的情况

 
  1. alert(fun3 === fun4); 

上面的结果是无论如何都超过了初学者的想法:在IE中式false,在Chrome或FireFox中是错误

 

对于Chrome或FireFox原因比较明显,也就是说,fun4被赋值后就立即抛弃了

 
  1. alert(typeof fun3); //function  
  2. alert(typeof fun4); //undefined 

在IE中

 
  1. alert(typeof fun3); //function  
  2. alert(typeof fun4); //function 

这一点是IE的错误,但是从结果可以得知:命名函数表达式的名字在外部作用域是无效的。哪怕如下,你执行下fun3还是不行的

 
  1. var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式  
  2.  
  3. }  
  4.  
  5. fun3();  
  6.  
  7. alert(typeof fun3); //function  
  8. alert(typeof fun4); //undefined 

虽然IE是有错误,但是IE透露了一个信息,非匿名的函数表达式由于函数有名称,被优先的分配了。看看我们最上面的函数定义声明的案例中的fun4

 

这么一来,如果你一不小心的话

 
  1. function fun1() {  
  2.     alert("A");  
  3. }  
  4.  
  5. var count = 2;  
  6.  
  7. var input = 10;  
  8. switch (input) {  
  9.     case 5:  
  10.         function fun1() {  
  11.             alert(5);  
  12.         }  
  13.         break;  
  14.     case 10:  
  15.         if (count % 2 == 0) {  
  16.             function fun1() {  
  17.                 alert("odd");  
  18.             }  
  19.         }  
  20.         else {  
  21.             function fun1() {  
  22.                 alert("even");  
  23.             }  
  24.         }  
  25. }  
  26.  
  27. fun1(); 

这些代码的执行会不会出乎你的意料呢?注意上面的代码在不同的浏览器中得到的是不同的。再看看下面的代码

 
  1. var fun1 = function(x) {  
  2.     if (x < 10) {  
  3.         return function fun2() {  
  4.             alert("min");  
  5.         }  
  6.     }  
  7.     return function fun2() {  
  8.         alert("max");  
  9.     }  
  10. }  
  11.  
  12. fun1(4)();  
  13. fun1(10)(); 

你是不是认为执行的结果又min也有max在不同的浏览器中,错了,这次他们都很正常的预期。说明return将后面的非匿名函数表达式有效的让编译器作为表达式而不是声明来看待了。
那么非匿名的函数表达式有什么问题嘛?
我们来看一个demo

 
  1. var fun1 = function fun2() {  
  2.     alert("OK");  
  3. }  
  4.  
  5. fun1(); // OK  
  6. fun2(); //在IE中弹出OK,在Chrome和FireFox中错误 

既然只有IE支持,那我们在IE上继续点实现,学习一个种语言,千万不要被书上的条条框框限制,最好是多多多多的天马行空的乱想点不正经的代码出来,有静态语言经验的人都会被下面的代码的执行结果吓死

 
  1. var fun1 = function fun2() {  
  2.     alert("OK");  
  3. }  
  4.  
  5. fun1 = function() {  
  6.     alert("fun1");  
  7. }  
  8.  
  9. fun1(); //fun1  
  10. fun2(); //OK 

fun1和fun2竟然不是指向同一个对象,或者说内存地址。所以,如果说这个是IE的bug,那么我们幸好Chrome和FireFox不支持非匿名的函数表达式具有实在的意义,上帝保佑,这样我们尽可能的不要写出非匿名的函数表达式就可以避免很多问题了。不过也有说法是说,非匿名的函数表达式在递归的时候有用,以下代码告诉我们匿名函数表达式也一样可以递归的哦,亲。

一个匿名函数使用递归的阶乘demo

 
  1. alert((function(x) {  
  2.     if (x > 1) {  
  3.         return x * arguments.callee(--x);  
  4.     }  
  5.     else {  
  6.         return 1;  
  7.     }  
  8. } (10)));// 3628800 

以上说了一大堆,就是为了要告诉你一个关键事实:函数表达式只能在代码执行阶段创建而且不存在于变量对象中,换个更通俗的说法是:函数表达式只有当执行到的时候,其才存在,否则是当他不存在的。

我们用匿名函数除了是return和内部赋值外,还常常用来做一次性执行的自执行函数。以下就匿名函数自执行的demo

 
  1. (function() {  
  2.     alert("OK");  
  3. })();  
  4.  
  5.  
  6. (function() {  
  7.     alert("OK");  
  8. } ()); 

上面的两种写法在所有浏览器中都可以执行,随你喜欢什么方式,只要固定就好,有些书推荐使用方式2,我不清楚为什么,反正对我来说很随意的使用了。
匿名函数也可以传参

 
  1. (function(x) {  
  2.     x--;  
  3.     alert(x);  
  4. } (10)); 

匿名函数往往非常好用,我们可以把匿名函数作为一个参数来传递,有时候,我们传递的的确是函数的本身,有时候我们传递的是函数的结果,如果是函数的结果,那么函数的自调用就非常cool了

先看看传递函数,以下的代码会生成一组li元素,并且背景色是依据前一个li的背景色加一点点,这样看上去就是一个渐变区域。当然,真的画渐变区域不必这样处理。

 
  1. $(  
  2. function() {  
  3.     $(":button").click(  
  4.     function() {  
  5.         for (var i = 0; i < 10; i++) {  
  6.             var ol = $("<li>").css({ width: 30, height: 30 }).  
  7.             addClass("left").  
  8.             appendTo("ol");  
  9.             ol.css("backgroundColor",  
  10.                 function(index, value) {  
  11.                     index = $("ol>li").index(this);  
  12.                     var r, g, b = 0, colorValue = "";  
  13.                     if (index == 0) {  
  14.                         r = parseInt(Math.random() * 256);  
  15.                         g = parseInt(Math.random() * 256);  
  16.                         b = parseInt(Math.random() * 256);  
  17.                     }  
  18.                     else {  
  19.                         colorValue = $("ol>li").eq(index - 1).css("backgroundColor").  
  20.                                     toString().replace("rgb(""").replace(")""")  
  21.                         r = parseInt(colorValue.split(",")[0]);  
  22.                         g = parseInt(colorValue.split(",")[1]);  
  23.                         b = parseInt(colorValue.split(",")[2]) + 5;  
  24.                     }  
  25.                     return "rgb(" + r + "," + g + "," + b + ")";  
  26.                 }  
  27.                 );  
  28.         }  
  29.     }  
  30.     );  
  31. }  
  32. ); 

我们再看一个例子,匿名函数的自调用在传参的使用的用处。我们点击一个div的时候,我创建了一个新的div,这个div的样式完全的复制了被点击的div的样式,注意我的css函数中传递了一个匿名的自调用函数,这个函数返回了一个对象

 
  1. $(  
  2. function() {  
  3.     $("div").click(  
  4.     function() {  
  5.         $("<div>").appendTo("body").css(  
  6.         (function(e) {  
  7.             var styleObj = {};  
  8.             for (var item in e.style) {  
  9.                 if (e.style[item] != "") {  
  10.                     styleObj[item] = e.style[item];  
  11.                 }  
  12.             }  
  13.             return styleObj;  
  14.         } (this))  
  15.         );  
  16.     }  
  17.     );  
  18. }  
  19. ); 

 本文转自shyleoking 51CTO博客,原文链接:http://blog.51cto.com/shyleoking/803085



相关文章
|
11天前
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
|
5天前
|
JavaScript 前端开发
JavaScript 函数语法
JavaScript 函数是使用 `function` 关键词定义的代码块,可在调用时执行特定任务。函数可以无参或带参,参数用于传递值并在函数内部使用。函数调用可在事件触发时进行,如用户点击按钮。JavaScript 对大小写敏感,函数名和关键词必须严格匹配。示例中展示了如何通过不同参数调用函数以生成不同的输出。
|
7天前
|
存储 JavaScript 前端开发
JS函数提升 变量提升
【10月更文挑战第6天】函数提升和变量提升是 JavaScript 语言的重要特性,但它们也可能带来一些困惑和潜在的问题。通过深入理解和掌握它们的原理和表现,开发者可以更好地编写和维护 JavaScript 代码,避免因不了解这些机制而导致的错误和不一致。同时,不断提高对执行上下文等相关概念的认识,将有助于提升对 JavaScript 语言的整体理解和运用能力。
|
2月前
|
JavaScript 前端开发 小程序
一小时入门Vue.js前端开发
本文是作者关于Vue.js前端开发的快速入门教程,包括结果展示、参考链接、注意事项以及常见问题的解决方法。文章提供了Vue.js的基础使用介绍,如何安装和使用cnpm,以及如何解决命令行中遇到的一些常见问题。
一小时入门Vue.js前端开发
|
14天前
|
存储 JavaScript 前端开发
前端开发:Vue.js入门与实战
【10月更文挑战第9天】前端开发:Vue.js入门与实战
|
16天前
|
JavaScript 前端开发
js教程——函数
js教程——函数
18 4
|
14天前
|
自然语言处理 JavaScript 前端开发
JavaScript高级——ES6基础入门
JavaScript高级——ES6基础入门
15 1
|
15天前
|
存储 JavaScript 前端开发
js中函数、方法、对象的区别
js中函数、方法、对象的区别
13 2
|
18天前
|
JavaScript 前端开发 Java
【javaScript数组,函数】的基础知识点
【javaScript数组,函数】的基础知识点
20 5
|
18天前
|
JavaScript 前端开发
Node.js 函数
10月更文挑战第5天
19 3