匿名函数
除去函数声明外的其他函数创建方式都可以创建匿名函数,如函数方法,对象字面量函数。
JavaScript的强大之处依赖于是否将其作为函数式语言进行使用,所以在大量函数中如果使用匿名函数会节省很多不必要的变量名,方便重要的函数的调用。
内联函数:给匿名函数进行命名,也就是在可以命名也可以匿名的函数中选择匿名的方式。
内联函数的作用域:内联函数只有在自身函数的内部才是可见的,就像作用域里的变量名称那样,他们的作用域仅限于自身函数内。
当对象的a属性= 对象b的属性,如果对象b的属性改变了,对象a的属性也不会改变。
但是,a=b,如果b改变了那么a也会改变。
”!!“可以将JavaScript表达式转化为与其等值的布尔值。如!!0===false !!; “help”===true.
对递归函数最好给函数命名而不要使用匿名函数,防止非直接的引用导致引用丢失。
将函数视为对象
函数也可以有属性、有方法,也可以享有所有普通对象所拥有的特性,而且还拥有一个超级特性:它们可以被调用。
函数存储:利用函数的属性特性给需要添加的函数添加一个附加属性从而实现快速判断该函数是否已经存在于数组中,如果不存在就添加进来,并给他添加一个属性。
var store={
nextId:1,
cache:{},
add:function(fn){
if (!fn.Id){
fn.Id=nextId++;
return !!(store.cache[fn.Id]=fn);
}
}
};
自记忆函数:这种函数能够记忆先前计算的结果。也就是在函数内增加一个数组对已经计算过的内容进行存储。
如判断是否是素数的函数
function isPrime(value){
if ( !isPrime.answers) {
isPrime.answers={};
}
else{
if (isPrime.answers[value]!=null) {
return isPrime.answers[value];
}
else{
var prime=value!=1;
for (var i =2; i<value; i++) {
if(prime%i==0){
prime=false;
break;
}
}
return isPrime.answers[value]=prime;
}
}
}
创建伪数组:
var elems={
length:0,
lang:4,
add:function(elem){
Array.prototype.push.call(this,elem);
},
gather : function(id){
this.add(document.getElementById(id));
}
};
elems.gather("first");
这样就既可以满足对所需内容的存储,并且可以给这个数组添加属性或者方法非常方便。(这里的length会默认地在每次add进一个新dom元素后+1)。
使用apply()支持传入数组作为参数
比如在使用Math.min()的时候只能传入参数并且“,”分隔开,Math.min(1,2,3);----1 如果想要传入参数为一个数组,找出数组中的最小值的话就可以使用apply()改支持参数类型为数组。
function smallest(array){
return Math.min.apply(this,array);-----apply第一个参数为this相当于是函数上下文指向不改变,第二个参数为传入的参数为smalest的参数。
}
var a=[7,1,3,4,5,2];
console.log( smallest(a) );
使用arguments进行函数重载
遍历可变长度的参数列表:
function merge(root){
for (var i = 1; i<arguments.length; i++) {
for (var key in arguments[i]) {
root[key]=arguments[i][key];----在对象root中添加key对应的属性名与其属性值
}
}
return root;
}
var merged=merge({name:"Batou"},{city:"Niihama"});
console.log(merged);------{name: "Batou", city: "Niihama"}
对于检测已经命名的形参是否已经传入,可以使用形参名===undefined进行判断,为true则没有传入,为false则已经传入。
需要注意的是arguments并完全是一个数组,他是一个类数组的东西,比如他可以像数组一样的去被遍历,但是却没有数组的那些方法,比如slice()。
如果我们需要对arguements使用数组的那些方法,可以参考以下例子:
function multiMax(multi){
return multi*Math.max.apply(this,Array.prototype.slice.call(arguments,1));
}
console.log(multiMax(5,2,3,4,5,6));----5*6=30;
函数的length属性:所有的函数都有length属性
通过length属性,可以知道声明了多少个命名参数(可以通过名字访问到的参数,也就是在function后的()里声明的参数)
通过arguments.length可以知道在调用时一共传入了多少个参数(包括命名的和未命名的参数)。
利用参数个数进行重载
function addMethod(object,name,fn){
var old =object[name];--------装载前一个函数,如果不符合要求则使用前一个函数
object[name]=function(){------这是一个判断函数,判读:如果需要参数个数等于实际参数个数,则调用这个新函数,如果不是那就调用●那个判断函数,类似于递归,一层层往回调用,直到判断函数满足条件为止。
if (fn.length==arguments.length) {--------因为是在另一个函数的作用域内,所以arguments也不再是函数addMethod的arguments,这个应该是fn的arguments。这里的意思则是如果fn的需要形参数量和实际传入参数数量相同
return fn.apply(this,arguments);----调用新函数
}
else if (typeof old=="function") {-----往回查找
return old.apply(this,arguments);
}
};
}
var ninja={
values:["Dean","Sam","Alex"]
};
addMethod(ninja,"find",function(name){
var ret ;
for (var i = 0; i<this.values.length; i++) {
if (this.values[i].indexOf(name)==0) {
ret=this.values[i];
}
}
return ret;
});
console.log( ninja.find("Sam") );-----1
很重要的一点是:arguments的指向是:在哪个函数function(){}的里面就是指向哪个函数的参数。
判断一个对象是否是函数
function isFunction(fn){
return Object.prototype.toString.call(fn)==="[object Function]";
}
(IE中虽然没有直接的call和apply方法,但是object上有call)