说明
在看这篇博文之前还是希望读者阅读本系列前几篇文章,还有就是该系列需要读者拥有其它语言的编程基础,一些基本的知识点,比如什么是形参和实参将不再赘述。这篇博文主要讲函数。
函数的定义
在js种支持函数的嵌套定义,也就是说函数内部还能定义函数,这样一来就形成了闭包,它将给js带来非常强大的编程能力。在js种定义可以有两种形式,一种是声明方式定义函数,它和其它语言中常用的定义差不多,还有一种以表达式方式定义函数,它和和c语言中将函数赋值给函数指针的样子有点像。
1 //声明方式定义函数 2 function distance(x1, y1, x2, y2){ 3 var dx = x2 - x1; 4 var dy = y2 - y1; 5 return Math.sqrt(dx*dx + dy*dy); 6 } 7 8 //表达式方式定义函数 9 var square = function(x){ 10 return x*x; 11 } 12 13 //表达式方式也可以拥有名字,这在递归的时候很有用 14 var f = function fact(x){ 15 if(x<=1) return 1; 16 return x*fact(x-1); 17 }
需要注意的是,这两种函数的定义方式存在些许的区别,声明方式只能在全局或者函数内部定义函数,但是这种方式定义的函数可以被与它同一作用域但是在它之前的代码所调用,因为在js中所有的声明都会被提前,关于声明提前的知识请参考这篇博客。表达式方式不仅能在全局还有函数内定义新的函数,而且能在if/for/...等语句块内定义函数,不过表达式方式定义的函数只会将var这个声明提前,而函数的定义实体(赋值这个动作)依然没有被提前,所以在它之前的代码不能调用表达式方式定义的函数。
倘若定义的函数没有return语句,或者return空值,那么调用者将得到这次调用的返回值为undefined。
函数的调用
函数的调用方式有以下四种
1 //作为函数被调用 2 alert("hello"); 3 4 //作为方法被调用 5 var a=[2,5,3]; 6 a.sort(); 7 8 //作为构造函数被调用 9 var a = new Array(3); 10 11 //被call()和apply()间接调用(下面分别通过call和apply调用toString函数) 12 Object.prototype.toString.call(new Date()); 13 Object.prototype.toString.apply(new Date());
变参函数
arguments
这个东西从c语言开始就一直伴随我们,当我们在c语言中学的第一条语句使用printf时,你是否想过printf是一个变参函数(参数长度、类型都不确定)。js同样也支持变参函数,不过其实现和c语言稍有不同。在js中所有的实参将封装成类数组对象(类数组对象一个普通的对象,不过它的属性名以数组索引的方式定义,js好多地方都用到了类数组对象)。
1 function max(){ 2 var ret=arguments[0]; 3 for(var value of arguments){ //of关键字在ECMAScript6中才支持 4 if(value > ret) 5 ret = value; 6 } 7 //console.log(arguments instanceof Array) //-->false 8 return ret; 9 } 10 11 var maxValue = max(5, 7, 5, 56, 21, 0); 12 console.log(maxValue);
在上面的代码中所有的实参被封装到arguments这个类数组对象中,这个类对象数组也像数组一样支持用下标方式访问(比如上面代码第二行),它也含有length这个属性以便知道参数的个数,不过如果你的浏览器比较新,你可以使用ECMAScript6中的新语法of以直接得到对象的属性值。
callee和caller
如果你的英语单词中有employee和employer等单词,你可能已经明白这两个东西的意思了。其中callee表示当前正在执行的函数,caller表示调用这个正在执行的函数的函数,也就是说caller指代调用者。可惜在严格模式下的ECMAScript5中,这两个东西不让使用,不过在普通模式下它们非常有用,比如匿名函数的递归。
1 var factorial = function(x){ 2 if (x<=1) return 1; 3 return x*arguments.callee(x-1); 4 }
自定义函数属性
函数在js中也是一个对象,如果你看过本系列之前的博客,基本上会见着不少于三次强调js中只有6中类型,其中函数是对象类型。这样一来函数也可以拥有自己的属性,有时候我们编程需要某一个函数在多次调用时共享一些数据,这就需要定义全局变量,然而全局变量定义多了以后会很难管理,因此我们更愿意定义函数属性。
1 //带缓存功能的阶乘计算函数 2 3 factorial[1]=1;//对这个不明白的同学去看上文推荐阅读的声明提前的博客。 4 function factorial(n){ 5 if(isFinite(n) && n>0 && n==Math.round(n)){ 6 if(!(n in factorial)) 7 factorial[n] = n*factorial(n-1); 8 return factorial[n]; 9 } 10 else return NaN; 11 }