JavaScript函数

http://xiayuanfeng.javaeye.com/blog/(原文)

 

什么是函数(Function)

function sum(a,b){  

     return a+b;  

}  

其实通俗的说就是一个有名称的代码段,方便重用。

要注意的是:

1.Javascript 的函数语法,因为Javascript本身就是区分大小写的,所以function不能写作Function或FUNCTION.

2.sum是函数的名称,这个并不是必须的,等以后我们会说到。

3.return是返回,如果不写的话,函数的返回是undefined.如果要返回多个值,可以返回个数组或者对象(以后再说)

函数的调用

 

下面我们讲函数的基本调用。

 

var result = sum(1,2)  

函数的参数

 

不好意思的说,这个才是本篇文章的重点。

实例一,参数少于 实际定义参数的数目

var result = sum(1);  

结果result 为NaN,这就说明了,参数b的值为undefined,但并不会报错,这就无形中制造了bug.

实例二,参数多于 实际定义参数的数目

sum(1,2,3,4,5)  

结果为3.发现多于的参数被忽略了。

实例三,没有参数的函数

function args(){return arguments;}  

每个函数里都有一个默认的数组那就是arguments .它就是每个函数默认的参数为[] 。如果我们调用函数如下

args(1,2,3,4,5,6);  

会发现arguments的值为[1,2,3,4,5,6]。这下容易了,我们可以改造下上面的sum方法

function sum(){  

    var res= 0;  

    for(i=0;i<arguments.length;i++){  

        res+=arguments[i];  

    }  

    return res;  

}  

sum(1,2,3,4);  

结果为10,这个sum函数灵活多了吧。^_^

 

Functions are data

 

这一篇是函数里比较重要的概念,那就是函数是一个数据。看个例子

function f(){return 1;}  

var f=function(){return 1;}  

这两个函数定义都是相同的。

typeof f;  

f的值为"function",所以说Javascript 的 函数是个数据类型。它有比较两个重要的特点

1.它包含了代码

2.能被执行

看个例子

var sum = function(a,b){return a+b;}  

var add = sum;  

sum=undefined;  

typeof sum;  

typeof add;  

add(1,2);  

我们把函数sum做为一个值赋给了add,发现删除了sum不影响add的调用。所以函数也就是个正常的赋值。

匿名函数(Anonymous Functions)

Javascript中,可以不用写赋值的代码,如

"abcd" 1 [1,2,3]  

这样都不会报错,这些数据叫做匿名的。同样的函数做为数据也可以是匿名的

function(a){return a}  

匿名函数的作用有两个

1.可以把匿名函数做为一个参数传入到另一个函数中。

2.你可以理解运行这个匿名函数

下面就会详细讨论这两个功能的作用了。

 

回调函数(Callback Functions)

 

因为函数和其他数据一样可以被赋值,删除,拷贝等,所以也可以把函数作为参数传入到另一个函数中。

实例一

function invoke_and_add(a,b){  

  return a()+b();  

}  

function one(){  

  return 1;  

}  

function two(){  

  return 2;  

}  

 

invoke_and_add(one ,two);  

结果为3;

再来看看匿名函数的用法。

实例二

invoke_and_add(function(){return 1;},function(){return 2;})  

我们用匿名函数替代了 one,two两个函数。

通过上面两个实例,回调函数的定义为:传递一个函数A到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称,就叫做匿名回调函数

 

回调函数的作用

 

主要有三个

1.当函数作为参数传递的时候,不用为这个函数定义一个名称,这样的好处是减少了全局变量。

2.节省了大量的代码。

3.提高了程序的性能。

 

自调用函数(Self-invoking Functions)

 

自调用函数也是匿名函数的一种表现形式,这个函数在定义之后,直接调用。如下

(  

 function(){  

   alert('haha');  

 }  

)()  

看起来还挺怪异,不过确实很简单。

自调用函数很方便使用,可以不用定义更多的全局变量。还有个好处,就是这个函数不能被执行两遍。真是非常适合做初始化的工作。许多著名的javascript库特别多的源码中使用了这个功能,例如本人喜欢的Jquery.

 

内部函数(Inner Functions)

 

把函数作为一个值来思考一下,既然一个值可以定义在函数中,把函数做为数据放在函数中也未尝不可。如下:

function a(param){  

 function b(theinput){  

   return theinput *2;  

 }  

 return 'The result is '+b(param);  

}  

 

也可以这么写

var a = function(param){  

     var b = function(theinput){  

        return theinput*2;  

     };  

     return 'The result is '+b(param);  

};  

 b函数是在a函数之中的 ,也就是意味着,在a函数的外部是无法访问b函数的。所以也称之为私有函数(private function)

a(2);  

a(8);  

b(2);  

 发现b(2)是没有定义的。也就确定了它确实是私有函数。

 内部函数的是使用还是有很多好处的。

1.可以有更少的全局变量。过多的使用全局变量就有可能由于命名冲突而产生过多的bugs

2.私有性,可以设计更好的接口函数供外部访问。

 

返回值为函数的函数(Functions that Return Functions)

 

在前几篇文章已经介绍了函数要有返回值,即使没有写return,函数也会返回一个undefine。

接下来看看返回值为函数的情况

function a(){  

 alert('a');  

 return function(){  

  alert('b');  

 };  

}  

 在这个例子中,a函数执行了alert('a'),以及它返回了另一个函数b。关于返回b的调用我们可以这样来用。

var newFunc = a();  

newFunc();  

 执行结果为 alert a和alert b

 如果不想赋值调用这个函数也可以简写如下

a()();  

 函数的自身重写

 

 因为函数可以返回一个函数,那就意味着可以用一个新的函数替代一个旧的函数,根据前一个例子来改进一下

a=a();  

 第一次运行函数a,就alert a,再次运行函数a,就alert b,这个功能对初始化非常有用。这个函数a重写了自己,避免在以后调用初始化的功能(上个例子为alert a)。

当然我们还有更简单的方法来重写a函数那就是在a函数的内部重写它,来看看代码

function a(){  

 alert("a")  

 a=function(){  

   alert("b");  

 }  

}  

 只有在初次调用a函数才会执行 alert a 在以后的调用中,都会执行alert b

 下面结合前几节看个综合的例子

var c = function(){  

   function a(){  

    alert('a')  

   }  

 

   function b(){  

    alert('b')  

   }  

   a();  

   return b;  

}();//alert('a');  

c();//alert('b');  

 这个例子有以下几点需要注意

 1. a函数,b函数是内部函数。

 2. return b 返回的是个函数的引用。

 3. 子调用函数重写了函数c。

 

 如果能明白这个例子,关于内部函数,子调用函数和返回值为函数的函数就可以都理解了。

 

闭包(Closures)闭包属于比较难的一部分,在学习闭包之前,先来学习以下Javascript的作用域(Scope)

 

作用域链(Scope Chain)

 

函数内部定义的变量,在函数外不能访问,或者变量定义在代码段中(如if,for中),在代码段之外也不可访问。

var a =1;  

function f(){  

 var b=1;  

 return a;  

}  

f();//a=1  

b;//b 没有定义  

 a 是全局变量,而b定义在函数f之中。所以:

 在f函数中,a和b都可以访问。

 在f函数之外,a可以访问,而b不可以

 

 再次看个例子

var a = 1;  

function f(){  

 var b = 1;  

 function n(){  

  var c =3;  

 }  

}  

 如果定义一个函数n在函数f中,函数n不但可以访问自己作用域的c,而且还能访问所谓的父作用域的b和a

 这就是作用域链(Scope Chain)

 词法作用域(Lexical Scope)

 

在Javascript中,也有词法作用域(Lexical Scope),这个意思是,函数在定义的时候就生成了它的作用域,而不是在调用的时候,看个例子就明白了。

function f1(){var a=1;f2();}  

function f2(){return a;}  

f1();//a没有定义  

先看看函数f1,调用了函数f2,因为函数局部变量a也在f1中,可能期望函数f2也访问a,但事实却不是这样。

因为这个时候f2函数已经定义完毕了,而它的范围没有a被找到。不管是函数f1还是函数f2,仅仅能访问的是本身的局部变 量或者全局变量。

 

用闭包来破坏这个作用域链(Breaking the Chain with Closure)

 

让我们举例来说明闭包吧。

实例1

 

function f(){  

 var b="b";  

 return function(){  

  return b;  

 }  

}  

 函数f包含了变量b,在全局作用域中不可能访问b,结果为没有定义(undefined)。

 看看这个函数的返回值为一个函数。这个新的函数可以访问f范围中的变量b。看如下代码

var n = f();  

n();//访问b  

 由于函数f本身就是全局的,而它返回的函数又是个新的全局函数,而它又可以访问函数f的作用域。

 

实例2

这个例子和上个例子结果是一样的,但是有一点不同,函数f并不返回一个函数,而是新建一个全局变量n,代码如下

var n;  

function f(){  

 var b = "b";  

 n=function(){  

  return b;  

 }  

}  

 所以可以直接n()来访问函数f里的变量b

 

通过上面两个例子我们就可以说当一个函数指向了它的父作用域,就可以称之为闭包。

当我们创建了个传递参数的函数f,这个参数就变成了函数f的局部变量了。我们可以创建一个f的内部函数来返回这个参数

function f(arg){  

 var n =function(){  

   return args;  

 }  

  arg++;  

 return n;  

}  

 

var m= f(123);  

m();//124  

 

闭包在循环中的应用

 

再循环中很容易引起一些bug,虽然表面是正常的。

看如下的代码

function f(){  

    var a = [];  

    var i;  

    for(i=0;i<3;i++){  

        a[i] = function(){  

            alert(i);  

            return i;  

        }  

    }  

    return a;  

}  

 

var a= f();  

a[0]();//3  

a[1]();//3  

a[2]();//3  

新建个循环,我们的目的是每次循环都新建一个函数,函数返回的是循环的序列值也就是i。我们看看以上代码的运行结果

都是为3.而我们期望的结果是1,2,3。

到底是为什么呢?我们新建了三个闭包,都指向了变量i。闭包并没有记住变量i的值,它仅是变量i的引用。在循环结束后,i的值是3,所以结果都是3了。

 

来看看正确的写法

function f() {  

  var a = [];  

  var i;  

  for(i = 0; i < 3; i++) {  

    a[i] = (function(x){  

      return function(){  

        alert(x);  

        return x;  

      }  

    })(i);  

  }  

  return a;  

}  

 

var a = f();  

a[0]();//0  

a[1]();//1  

a[2]();//2  

 我们又用了一个闭包,把变量i变成了局部变量x了,x变量每次都是不同的值了。如果没有彻底理解自调用函数那就如下写法就可以明白了

function f() {  

  function makeClosure(x) {  

    return function(){  

      return x;  

    }  

  }  

  var a = [];  

  var i;  

  for(i = 0; i < 3; i++) {  

    a[i] = makeClosure(i);  

  }  

  return a;  

}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值