递归函数
-
递归函数必须满足两个条件:
- 调用自身
- 有终止条件
-
作为普通命名函数的递归
-
作为对象方法的递归(匿名函数):可能导致引用丢失的问题
var obj={ chrip:function(n){ return n>1?obj.chrip(n-1)+"-chrip":"chrip"; } } obj.chrip(5); //chrip-chrip-chrip-chrip-chrip var newObj={ chrip:obj.chrip } newObj.chrip(5); //chrip-chrip-chrip-chrip-chrip obj={}; newObj.chrip(5); //undefined; **此处obj.chrip丢失**解决引用丢失的方法:使用this或使用内联函数
-
使用this:要求obj和newObj的方法名称必须都为chrip
var obj={ chrip:function(n){ return n>1?this.chrip(n-1)+"-chrip":"chrip"; } }; -
参照以下内联函数
-
-
作为内联函数的递归:给方法的匿名函数起一个名称.内联函数的函数名称只有在自身函数内部可见
var obj={ chrip:function sound(n){ return n>1?sound(n-1)+"-chrip":"chrip"; } } var newObj={ chripSound: obj.chrip }
函数是第一型对象
因为函数为第一型对象,所以函数可以有自己的属性。用途:
-
函数存储:最常见的是事件回调管理.
下例根据函数本身的属性来判断函数是否被存储
var store={ nextId:1, cache:{}, add:function(fn){ if(!fn.id){ fn.id=this.nextId++; return !!(this.cache[fn.id]=fn); // !!将表达式强制转换为等效布尔值 } } }; function fn(){}; assert(store.add(fn),"fn is saved","fn is not saved"); //fn is saved assert(store.add(fn),"fn is saved","fn is not saved"); //fn is not saved -
自记忆函数:通过函数属性来保存计算结果,提高性能。
下例通过函数的answer属性来缓存计算结果
function isPrime(value){ if(!isPrime.answer) isPrime.answer={}; if(isPrime.answer[value]!=null){ return isPrime.answer[value]; } var prime=value!=1; for(var i=2;i<value;i++){ if(value%i==0){ prime=false; break; } } return isPrime.answer[value]=prime; } -
模拟数组:有些时候一个数据集合可能需要包含一些元数据属性。这里我们介绍将对象当作数组来操作。 当然我们也可以把数组添加属性或方法来处理这种情况。这里我们只演示把对象当数组。
var elems={ length:0, add:function(elem){ Array.prototype.push.call(this,elem);//强制把elems当作数组 }, gather:function(id){ this.add(document.getElementById(id)); } } elems.gather("first"); elems.gather("second"); console.log(elems.length);//2,此处把elems对象当作数组来处理,其length属性是伪数组的数据长度
函数的重载
JS中没有强制函数声明多少参数就得传入多少参数。函数能否成功处理这些参数完全取决于函数本身。基于这一点,可以根据传入参数的不同对函数进行重载。
-
使用if-else类处理不同的语句_当逻辑复杂是,函数本身会显的非常笨拙
function(){ if(arguments.length==0){//do something} else if(arguments.length==1){//do something} else if(arguments.length==1){//do something} .. } -
利用闭包的性质进行重载
//Part1:定义重载的对象以及实现重载的方法 var overloading={}; function addMethod(object, name, fn){ var old=object[name]; object[name]= function(){ if(fn.length===arguments.length){ return fn.apply(this,arguments); } else if(typeof old=='function'){ return old.apply(this, arguments); } } }; //part2:对logging方法进行4次重载 addMethod(overloading,'logging',function(){console.log('no argument');}); addMethod(overloading,'logging',function(arg1){console.log('1 argument');}); addMethod(overloading,'logging',function(arg1, arg2){console.log('2 arguments');}); addMethod(overloading,'logging',function(arg1, arg2, arg3){console.log('3 arguments');}); //part3:测试4个重载的方法 overloading.logging(); //no arguments overloading.logging(1); //1 argument overloading.logging(1,2); //2 arguments overloading.logging(1,2,3) //3 arguments上例中利用了闭包的特性,来实现对logging方法的重载。这样做的好处是每次重载都使用了单独的匿名函数,从而便于维护和理解。
在Part1中,old和fn都利用了闭包,每次执行logging方法时,old和fn都是指向上一次保存的匿名函数。
在part2中,第四次重载完成后,object[name]中的fn指向的是第四次重载是定义的匿名函数,而old则是指向了第三次重载时object[name]的值;以此类推。
在Part3中,每次调用logging方法是,先是判断传入的参数是不是3个,如果不是再判断传入的参数是不是2个,以此类推。