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

来源:岁月联盟 编辑:exp 时间:2012-03-01

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

[html]
function fun1() {//声明一个函数 
 

 
function() { //声明一个匿名函数 
 

 
var fun2 = function() {//声明一个变量指向一个匿名的函数表达式 
 

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

 
function fun5() { 
    return function() { //返回一个匿名函数 
 
    } 

那function和var的区别有哪些?我们不讨论深奥的,我们看下哪些运行结果
[html]
alert(typeof fun1); //function 
alert(typeof fun2); //undefined 
alert(typeof fun3); //undefined 
alert(typeof fun4); //function 
alert(typeof fun5); //function 
 
 
function fun1() {//声明一个函数 
 

 
function() { //声明一个匿名函数 
 

 
var fun2 = function() {//声明一个变量指向一个匿名的函数表达式 
 

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

 
function fun5() { 
    return function() { //返回一个匿名函数 
 
    } 

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

 
function fun1() { 
    alert("N"); 

但事实上,这个代码是可以执行的,执行的结果暗示我们函数的名称会被自动的作为对象的一个属性,函数体就是属性的值了
[html]
fun1(); // n 
 
this["fun1"](); // n 
this.fun1(); // n 
window["fun1"](); // n 
window.fun1(); // n 

现在我们把两个因素合起来看看,function在所有执行代码前先编译,且后面的function会覆盖前面的function定义,那么,如下的代码执行的是?
[html]
var input = 5; 
switch (input) { 
    case 10: 
        function fun1() { 
            alert(10); 
        } 
        break; 
    case 5: 
        function fun1() { 
            alert(5); 
        } 
        break; 
    default: 
        function fun1() { 
            alert("default"); 
        } 
        break; 

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

而FireFox把if中的语句作为执行快,运行时才编译,所以他的结果是
[html]
fun1(); //5 
为了解决这个问题,你需要吧动态分配的函数以函数的表达式来执行,避免编译器对函数还没有执行到就已经分配了,比如

[html]
var input = 5; 
switch (input) { 
    case 10: 
        fun1 = function() { 
            alert(10); 
        } 
        break; 
    case 5: 
        fun1 = function() { 
            alert(5); 
        } 
        break; 
    default: 
        fun1 = function() { 
            alert("default"); 
        } 
        break; 

说到函数的表达式,那什么是函数的表达式呢?简单的说,在右边的都是函数的表达式
[html]
var fun1 = function() {//函数表达式 
 

 
var fun1 = function fun2() {//函数表达式 
 

 
var obj = { 
    Do: function() {//函数表达式 
 
    } 

 
new function() {//函数表达式 
 

 
function fun3() { 
    return function() { //函数表达式 
 
    } 

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

[html]
(function() { }); 
[function() { } ]; 

函数表达式和一般的表达式的值一样,都是执行到的时候才编译,不过有一个例外,就是非匿名的函数表达式
[html]
var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式 
 

上面的代码如果你没有特定的含义,求你最好不要这么写,因为会出现你想不到的情况
[html]
alert(fun3 === fun4); 

上面的结果是无论如何都超过了初学者的想法:在IE中式false,在Chrome或FireFox中是错误
对于Chrome或FireFox原因比较明显,也就是说,fun4被赋值后就立即抛弃了

[html]
alert(typeof fun3); //function 
alert(typeof fun4); //undefined 

在IE中
[html]
alert(typeof fun3); //function 
alert(typeof fun4); //function 
这一点是IE的错误,但是从结果可以得知:命名函数表达式的名字在外部作用域是无效的。哪怕如下,你执行下fun3还是不行的www.2cto.com
[html]
var fun3 = function fun4() {//声明一个变量指向一个非匿名的函数表达式 
 

 
fun3(); 
 
alert(typeof fun3); //function 
alert(typeof fun4); //undefined 

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

[html]
function fun1() { 
    alert("A"); 

 
var count = 2; 
 
var input = 10; 
switch (input) { 
    case 5: 
        function fun1() { 
            alert(5); 
        } 
        break; 
    case 10: 
        if (count % 2 == 0) { 
            function fun1() { 
                alert("odd"); 
            } 
        } 
        else { 
            function fun1() { 
                alert("even"); 
            } 
        } 

 
fun1(); 

这些代码的执行会不会出乎你的意料呢?注意上面的代码在不同的浏览器中得到的是不同的。再看看下面的代码
[html]
var fun1 = function(x) { 
    if (x < 10) { 
        return function fun2() { 
            alert("min"); 
        } 
    } 
    return function fun2() { 
        alert("max"); 
    } 

 
fun1(4)(); 
fun1(10)(); 

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

 
fun1(); // OK 
fun2(); //在IE中弹出OK,在Chrome和FireFox中错误 
既然只有IE支持,那我们在IE上继续点实现,学习一个种语言,千万不要被书上的条条框框限制,最好是多多多多的天马行空的乱想点不正经的代码出来,有静态语言经验的人都会被下面的代码的执行结果吓死
[html]
var fun1 = function fun2() { 
    alert("OK"); 

 
fun1 = function() { 
    alert("fun1"); 

 
fun1(); //fun1 
fun2(); //OK 

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

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

[html]
alert((function(x) { 
    if (x > 1) { 
        return x * arguments.callee(--x); 
    } 
    else { 
        return 1; 
    } 
} (10)));// 3628800 

 


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

我们用匿名函数除了是return和内部赋值外,还常常用来做一次性执行的自执行函数。以下就匿名函数自执行的demo
[html]
(function() { 
    alert("OK"); 
})(); 
 
 
(function() { 
    alert("OK"); 
} ()); 

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

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

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

[html]
$( 
function() { 
    $(":button").click( 
    function() { 
        for (var i = 0; i < 10; i++) { 
            var ol = $("<li>").css({ width: 30, height: 30 }). 
            addClass("left"). 
            appendTo("ol"); 
            ol.css("backgroundColor", 
                function(index, value) { 
                    index = $("ol>li").index(this); 
                    var r, g, b = 0, colorValue = ""; 
                    if (index == 0) { 
                        r = parseInt(Math.random() * 256); 
                        g = parseInt(Math.random() * 256); 
                        b = parseInt(Math.random() * 256); 
                    } 
                    else { 
                        colorValue = $("ol>li").eq(index - 1).css("backgroundColor"). 
                                    toString().replace("rgb(", "").replace(")", "") 
                        r = parseInt(colorValue.split(",")[0]); 
                        g = parseInt(colorValue.split(",")[1]); 
                        b = parseInt(colorValue.split(",")[2]) + 5; 
                    } 
                    return "rgb(" + r + "," + g + "," + b + ")"; 
                } 
                ); 
        } 
    } 
    ); 

); 

我们再看一个例子,匿名函数的自调用在传参的使用的用处。我们点击一个div的时候,我创建了一个新的div,这个div的样式完全的复制了被点击的div的样式,注意我的css函数中传递了一个匿名的自调用函数,这个函数返回了一个对象
[javascript]
$( 
function() { 
    $("div").click( 
    function() { 
        $("<div>").appendTo("body").css( 
        (function(e) { 
            var styleObj = {}; 
            for (var item in e.style) { 
                if (e.style[item] != "") { 
                    styleObj[item] = e.style[item]; 
                } 
            } 
            return styleObj; 
        } (this)) 
        ); 
    } 
    ); 

); 

摘自 shyleoking的专栏