函数视作高阶函数的条件
函数可作为参数被传递 或 函数可做为返回值输出
1.函数作为参数被传递:回调函数
回调函数应用:异步处理(ajax)、调用函数的内部数据“委托”回调函数处理(Array.prototype.sort)
2.函数做为返回值输出
var pointSingle = function(fn){
var single;
return function(){
// 返回已创建对象 或 返回创建对象的函数
return single || (single = fn.apply(this, arguments))
};
}
// 根据参数 生成点
var point = function(x,y){ return { left: x, top: y}; };
// 生成唯一点
var getPoint = pointSingle( point );
var point01 = getPoint(0,0);
var point02 = getPoint(10,10);
// point01 point02指向同一个 { left: 0, top: 0}为第一次创建的 point01 = getPoint(0,0);
console.log(point01 === point02);
console.log(point01); //{ left: 0, top: 0}
console.log(point02); //{ left: 0, top: 0}
其他应用:
实现AOP
(面向切片编程:把与核心业务逻辑模块无关的功能分离,动态加入业务逻辑,以保持业务模块的纯净和高内聚,且实现无关模块的复用。),如 日志功能等。
Function.prototype.setLog = function(before, after){
var _self = this;
return function(){
before.apply(this, arguments);
var ret = _self.apply(this, arguments);
after.apply(this, arguments);
return ret;
}
}
// 业务代码
var func = function(){
console.log("run func.");
}
// 给业务代码增加日志
func = func.setLog(
function(){ console.log("before ");},
function(){ console.log("after ");}
);
func();
// 顺序输出:
// before
// run func.
// after
实现函数节流
// 函数节流
var throttle = function(fn, interval){
var targFunc = fn, // 保存目标函数
timer, // 声明定时器
firstTime = true; // 是否第一次调用
return function(){
var args = arguments,
_this = this;
if (firstTime){ // 第一次调用,不检查定时器
targFunc.apply(_this, args);
return firstTime = false;
}
if(timer){ // 定时器未结束
return false;
}
timer = setTimeout(function(){
clearTimeout(timer);
timer = null; // 结束时置 null
targFunc.apply(_this, args);
}, interval || 500); // 未设置则默认为 500 ms
};
};
window.onresize = throttle(function(){
console.log(window.document.body.clientWidth);
}, 500);
实现分时函数(数据分批加载)
例:循环打印 有 1000 个元素的数组的各元素值
普通情况为一次打印完 1000 个数据;
分时则 可做 每隔一段时间 打印一部分;
实现:每1s 打印 10 个数据,共500个
// 分时函数
var timeChunk = function(arr, fn, interval, count){
var timer,
count;
count = count || 1; // 默认每次数量为1
// 每个间隔调用执行
var start = function(){
for(var i=0, len=arr.length; i < Math.min(count, len); i++){
var obj = arr.shift();
fn(obj);
}
};
return function(){
timer = setInterval(function(){
if (arr.length === 0) {
return clearInterval(timer);
}
start();
}, interval);
};
};
var arr01 = [];
for (var i =0; i<500; i++){
arr01.push(i + "-point");
}
var testTimeChunk = timeChunk(arr01, function(txt){console.log(txt);}, 1000, 10);
testTimeChunk();
惰性加载
实现浏览器相同功能不同封装的兼容封装
例:事件监听的封装对比.
// 事件监听的封装对比
// 1. 每次调用执行原函数,都会进行判断
var addEvent01 = function(ele, type, handler){
if(window.addEventListener){
return ele.addEventListener(type, handler, false);
}
if(window.attachEvent){
return ele.attachEvent("on"+type, handler );
}
throw new Error("not found addEventListener & attachEvent.");
};
// 2. 立即执行函数,一定会执行一次判断,返回后addEvent02 不会进行判断
var addEvent02 = (function(win){
if(win.addEventListener){
return function(ele, type, handler){ ele.addEventListener(type, handler, false); };
}
if(win.attachEvent){
return function(ele, type, handler){ ele.attachEvent("on"+type, handler ); };
}
throw new Error("not found addEventListener & attachEvent.");
})(window);
// 3. 当被调用时,内部进行判断,更改函数定义,之后调用均为更改后的函数
var addEvent03 = function(ele, type, handler){
if(window.addEventListener){
addEvent03 = function(ele, type, handler){
ele.addEventListener(type, handler, false);
};
}else if(window.attachEvent){
addEvent03 = function(ele, type, handler){
ele.attachEvent("on"+type, handler );
};
}else{
throw new Error("not found addEventListener & attachEvent.");
}
addEvent03(ele, type, handler);
};
部分代码引用和修改:《JavaScript 设计模式与实践》by曾探,