高阶函数是指至少满足下列条件之一的函数:
- 函数可以作为参数被传递
- 函数可以作为返回值输出。
一、函数作为参数传递
- 回调函数
例:ajax异步请求中的回调函数
var getUser=function(userId,callback){
&.ajax("http://xxx.com/getUser?"+userId,function(){
if(typeof callback==='function'){
callback(data);
}
})
};
getUser(0802,function(data){
console.log(data.userName);
});
- 当我们想在ajax请求返回之后做一些事情,但又不知道请求返回的确切时间时,最常见的方案就是把callback函数当作参数传入ajax请求的方法中,待请求完成之后执行callback函数
例:在页面创建100个节点,将节点均设置为隐藏
var appendDiv= function () {
for(var i=0;i<100;i++){
var div=document.getElementsByTagName("div");
div.innerHTML=i;
document.appendChild(div);
div.style.display="none";
}
};
appendDiv();
- 这样做不合理,会使其成为一个难以复用的函数,并不是每个人创建了节点之后就希望它们立刻隐藏
更改:将div.style.display="none";
这行代码抽出来,用回调函数的形式传入appendDiv方法:
var appendDiv=function(callback){
for(var i=0;i<100;i++){
var div=document.getElementsByTagName("div");
div.innerHTML=i;
document.appendChild(div);
if(typeof callback=="function"){
callback(div);
}
}
};
appendDiv(function(node){
node.style.display="none";
});
- Array.prototype.sort
把可变的部分封装在函数参数里,动态传入Array.prototype.sort
//从小到大
[1,4,3].sort(function(a,b){
return a-b;
})
//从大到小
[1,4,3].sort(function(a,b){
return b-a;
})
二、函数作为返回值输出
- 判断数据的类型
console.log(Object.prototype.toString.call([1, 2, 3])); //[object Array]
console.log(Object.prototype.toString.call("str")); //[object String]
console.log(Object.prototype.toString.call(123)); //[object Number]
例:将这些字符串作为参数提前植入isType函数,代码如下
var isType=function(){
return function(obj){
return Object.prototype.toString.call(obj);
}
};
var isString=isType('String');
var isArray=isType('Array');
var isNumber=isType('Number');
console.log(isArray([1, 2, 3])); //[object Array]
- getSingle
既把函数当作参数传递,又让函数执行后返回了另外一个函数
var getSingle=function(fn){
var ret;
return function(){
return ret || (ret=fn.apply(this,arguments));
}
};
var getScript=getSingle(function(){
return document.createElement("script");
});
var script1=getScript();
var script2=getScript();
console.log(script1 === script2); //true
三、高阶函数实现AOP
把一个函数“动态织入”到另一个函数中
Function.prototype.before=function(beforefn){ //beforefn为新添加的函数,装载了新添加的功能代码
var self=this; //保存原函数func的引用
return function(){ //返回包含了原函数和新函数的“代理”函数
beforefn.apply(this,arguments); //执行新函数,且保证this不被劫持
// 新函数接受的参数会被原封不动地传入原函数,新函数在原函数之前执行
return self.apply(this,arguments); //执行原函数并返回原函数的执行结果,并保证this不被劫持
}
};
Function.prototype.after=function(afterfn){
var self=this;
return function(){
var ret=self.apply(this,arguments);
afterfn.apply(this,arguments);
return ret;
}
};
var func=function(){
console.log(2);
};
func=func.before(function(){
console.log(1);
}).after(function(){
console.log(3);
});
func();
四、惰性加载函数
attachEvent——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera
addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8
例1:缺点:当它每次被调用时都会执行里面的if条件语句,增加了开销(虽然不大)
var addEvent=function(elem,type,handler){
if(window.addEventListener){
return elem.addEventListener(type,handler,false);
}
if(window.attachEvent()){
return elem.attachEvent("on"+type,handler);
}
}
例2:在代码加载时就立刻进行一次判断
var addEvent = (function(){
if (window.addEventListener) {
return elem.addEventListener(type, handler, false);
}
if (window.attachEvent()) {
return elem.attachEvent("on" + type, handler);
}
})();
- 可能我们从头到尾都没有使用过addEvent函数,这样会稍稍延长页面ready的时间
例3:惰性加载函数
var addEvent=function(elem,type,handler){
if(window.addEventListener){
addEvent=function(elem,type,handler){
elem.addEventListener(type,handler,false);
}
}
else if(window.attachEvent){
addEvent=function(elem,type,handler){
elem.attachEvent("on"+type,handler);
}
}
};
var div1=document.getElementById("div1");
addEvent(div1,"click",function(){
console.log(1);
});
addEvent(div1,"click",function(){
console.log(2);
});
- 在第一次进入条件分支后,在函数内部会重写这个函数,重写之后的函数就是我们期望的addEvent函数,在下一次进入addEvent函数时,addEvent函数就不再存在条件分支