匿名函数:就是没有名字的函数
如:
function(){assert(true,'power!')}
var ninja={shot:function(){assert(true,'Ninja!')}}
ninja.shot();
setTimeout(function(){assert(true,'Forever!')},500)
以上都是匿名函数,有些地方不需要函数名字基本匿名函数就出现了
而匿名函数的出现是让我们能够创建更小的执行单元,而不是创建大量命令式语句的函数
如果优雅的实现递归:
答案是:callee arguments有一个callee属性,callee属性引用的是当前所执行的函数
例子:
var ninja={
chirp:function(n){
return n>1?arguments.callee(n-1)+'-chirp':'chirp';
};
}
assert(ninja.chirp(3)=='chirp-chirp-chirp',"yes");
将函数视为对象
javascript中的函数和其他语言中的函数不同,javascript赋予了函数很多特性,最重要的特性将函数作为第一型对象
函数可以拥有属性和方法,变量,可以享有所有普通对象所拥有的普通对象所有特性,最重要的是能被调用
给函数添加属性:
var obj={};
var fn=function(){};
obj.prop="hitsuke(distraction)";
fn.prop="tanuki(climbing)"
函数储存:
var store={
nextId:1,
cache:{},
add:function (fn) {
if(!fn.id){
fn.id=store.nextId++;
return !!(store.cache[fn.id]=fn);
}
}
}
function ninja() {
}
console.log(store.add(ninja))
console.log(!store.add(ninja))
可以看到add(),先判断传入fn中是否有id,如果没有我们就在fn中添加id属性,同时用id做key和fn为value,保存了一个方法(ninja())
第二次存在id属性就不会再添加了
自记忆函数
缓存记忆是构建函数的过程,这种函数能够记住先前计算得结构。避免已经执行过的不必要复杂计算,这种方式能够可以显著提高性能
最牛逼的还能记忆dom元素…
先看一个例子:
function isPrime(value) {
if(!isPrime.anwers) isPrime.anwers={};
if(isPrime.anwers[value]!=null){
return isPrime.anwers[value];
}
var prime=value!=1;
for(var i=2;i<value;i++){
if(value%i==0){
prime=false;
break
}
}
return isPrime.anwers[value]=prime;
}
console.log(isPrime(5));
console.log(isPrime.anwers[5]);
可以看到isPrime中记忆了anwers属性,如果第二次传入时可以直接得到结果
其实相当于给这个函数做了一个缓存,带缓存的函数,如果用的记忆dom元素你想想能提高多少效率
function getElements(name) {
if(!getElements.cache)getElements.cache={};
return getElements.cache[name]=getElements.cache[name]||
document.getElementsByTagName(name);
}
书中说提高了5倍的效率!
可变长度的参数列表
使用apply支持可变参数
Math.max();
如果你想找到最大值,你就得这样
Math.max(1,2,3,4);
你就想能不能传入一个数组,让他自己找,其实javascript的数组方法也没有寻找最大值,而max方法又不支持数组,怎么办呢
还记得apply么,就是第三章介绍过他,现在还没忘记吧
apply的第一个参数就是指定的上下文this,第二就是数组,他可以把数组都变成参数传入方法
Math.max.apply(Math,array);
这里扩展一下,如果想把方法传入的可变参数中第一个和可变参数最大值相乘你会怎么做呢
还记得之前说的arguments是伪数组么,你是不能对他进行用slice方法的
这个时候你会想到啥?
没猜错就是call
function multi(mulit){
return mulit*Math.max.apply(Math,Array.prototype.slice.call(argument,1));
}
将argument伪装成数组去使用Array的slice方法
函数重载
重载呢,一般是同名不同参数,一般对于其他面向对象语言是这样的比如Java
而javascript不是这样
我们重载呢,只用一个实现,只不过这个实现内部是通过传入参数的特性和个数进行相应修改来达到目的
只是如果有其他语言的经验,我说道上面那句话,应该就知道怎么实现的
whatever:function(){
switch(arguments.length){
case 0:
//do some
break;
case 1:
//do some
break;
}
}
但是我们还是得像其他面向语言靠齐不是
这里要知道函数也是有属性叫做length的,不要跟argument的length混淆
通过length我们可以知道声明了多少命名参数
通过arguments.length,可以知道在调用时传入了多少参数
开工:
function addMethod(object, name, fn) {
var old=object[name];
object[name]=function () {
debugger
if(fn.length==arguments.length){
return fn.apply(this,arguments);
}else if(typeof old=='function')
return old.apply(this,arguments);
}
}
var ninja={
values:['one','two','three']
};
addMethod(ninja,'what',function () {
return this.values;
})
addMethod(ninja,'what',function (a) {
var ret=[];
for(var i=0;this.values[i].length;i++){
if(this.values[i].indexOf(a)==0)
ret.push(this.values[i]);
return ret;
}
})
addMethod(ninja,'what',function (a,b) {
var ret=[];
for(var i=0;this.values[i].length;i++){
if(this.values[i]==(a+''+b))
ret.push(this.values[i]);
return ret;
}
})
console.log(ninja.what().length==3);
console.log(ninja.what('one').length==1);
console.log(ninja.what('one','two').length==1);
console.log(ninja.what('one','two','three')==null);
先看addMethod方法第三个参数传入了匿名方法,参数有1,2,3个。
其中addMethod的主要逻辑就是object作为存储载体,name为key,value为匿名方法
如果仅仅这样是实现不了重载,我们继续看下面逻辑
function () {
if(fn.length==arguments.length){
return fn.apply(this,arguments);
}else if(typeof old=='function')
return old.apply(this,arguments);
}
按照执行代码逻辑解释一下代码思路
最后是传入了2个参数
addMethod(ninja,'what',function (a,b) {...}
仅仅调用addMethod是不会执行匿名方法的
真正去调用的是 下面的
console.log(ninja.what().length==3);
而执行这句代码时,匿名方法就被执行了,fn.length也就是2,因为最后确实传入了两个参数,
而命名参数和传入参数等于就会直接执行匿名方法,但是现在没有传入参数
当不等于时,执行
var old=object[name];
old.apply(this,arguments);
其实这里是被递归了,不断的去调用存储的几个匿名方法直到命名和传入参数相等,也就是之前存储的没有参数的匿名方法
不理解的可以把整块代码debug运行一遍