3.2高阶函数
2019-02-07 本文已影响0人
翊只仙女
资料整理:JavaScript设计模式与开发实践
高阶函数:函数可以作为参数被传递或作为返回值输出。
函数作为参数传递
- 回调函数
- Array.prototype.sort
sort()方法用原地算法对数组进行排序,并返回数组,默认排序是根据字符Unicode码点。sort()方法接受一个函数作为参数,这个函数封装了元素的排序规则。
函数作为返回值输出
让函数返回一个可执行的函数,意味着运算过程是可延续的。
- 判断数据类型
- getSingle
高阶函数实现AOP
AOP(面向切面编程)主要作用是把一些核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的的功能通常包括日志统计, 安全控制,异常处理等。把这些功能抽离出来之后,再通过动态织入的方式掺入业务逻辑模块之中。
//通过扩展Function.prototype来实现AOP
Function.prototype.before = function(beforefn) {
var self = this; //保留原函数的引用
return function() { //返回包含了原函数和新函数的‘代理’函数
beforefn.apply(this, arguments); //执行新函数,修正this
return self.apply(this, arguments); //执行原函数
}
}
Function.prototype.after = function(afterfn) {
var self = this;
return function() {
var ret = self.apply(this, arguments);//这里this指向的是全局
afterfn.apply(this, arguments);
return ret;
}
}
var func = function() {
console.log('center/');
};
func = func.before(function() {
console.log(1);
}).after(function() {
console.log(3);
});
func();
高阶函数的其他运用
-
currying
函数柯里化(function currying),又称部分求值,一个currying函数首先会接收一些参数,接收参数后并不会立刻求值,而是继续返回另外一个函数,之前传递的参数在闭包中被保存起来,待函数被真正需要求值的时候,之前所传参数会被一次性用于求值。
var currying = function(fn) {
var args = [];
return function() {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
[].push.apply(args, arguments);
return arguments.callee; //callee包含当前正在执行的函数
}
}
}
var cost1 = (function() {
var money = 0;
return function() {
for (var i = 0; i < arguments.length; i++) {
money += arguments[i];
}
return money;
}
})();
var cost2 = currying(cost1);
cost2(100);
cost2(200);
cost2(300);
cost2();
-
uncurrying
泛化this过程
Function.prototype.uncurrying = function() {
var self = this;
return function() {
console.log(arguments);//Arguments(2)[Arguments(3), 4]
var obj = Array.prototype.shift.call(arguments);//Arguments(3) [1, 2, 3,]
return self.apply(obj, arguments);
}
}
//另一种实现方式
Function.prototype.uncurrying = function() {
var self = this;
return function() {
Function.prototype.call.apply(self, arguments);
}
}
var push = Array.prototype.push.uncurrying();
(function() {
push(arguments, 4);
console.log(arguments);
})(1,2,3);
- 函数节流
- 函数被频繁调用的场景
window.onsize事件
mousemove事件
上传进度 - 函数节流原理
上述三个场景共同问题是函数触发频率太高,需要我们按事件忽略一些请求 - 代码实现
var throttle = function(fn, interval) {
var self = fn,//需要被执行的函数引用保存为_self
timer,//定时器
firstTime = true;
return function() {
var _me = this,
args = arguments;
if (firstTime) {
self.apply(_me, args);
firstTime = false;
return false;
}
if (timer) {
return false;
}
timer = setTimeout(function() {
clearTimeout(timer);
timer = null;
self.apply(_me, args);
}, interval || 500);
}
}
window.onresize = throttle(function(){ console.log( 1 ); }, 2000 );
-
分时函数
有些函数确实是用户主动调用的,但因为一些客观原因却会严重影响页面性能,例如创建webQQ好友列表,如果一个好友代表一个节点,当在渲染列表时,可能一次性创建成百上千个节点,在短时间内大量添加dom节点会让浏览器卡顿或假死
var timeChunk = function(ary, fn, count, interval) {
var obj,t;
var start = function() {
for (var i = 0; i < Math.min(count || 1, ary.length); i++) {
obj = ary.shift();
fn(obj);
}
}
return function() {
t = setInterval(function() {
if (ary.length === 0) {
return clearInterval(t);
};
start();
}, interval || 200);
}
}
var arys = [];
for (let i = 0; i <= 1000; i++) {
arys.push(i);
}
var renderList = timeChunk(arys, function(n) {
var div = document.createElement('div');
div.innerHTML = n;
document.body.append(div);
}, 8, 2000);
renderList();
- 惰性加载函数
var addEvent = function( elem, type, handler ){
if ( window.addEventListener ) {
return elem.addEventListener( type, handler, false );
}
if ( window.attachEvent ) {
return elem.attachEvent( 'on' + type, handler );
}
};
//普通通用绑定事件函数缺点在于每次调用时都会执行if条件分支,虽然分支开销并不大,但有方法可以让程序避免重复的执行过程
//方案二
var addEvent = (function(){
if ( window.addEventListener ){
return function( elem, type, handler ){
elem.addEventListener( type, handler, false );
}
}
if ( window.attachEvent ){
return function( elem, type, handler ){
elem.attachEvent( 'on' + type, handler );
}
}
})();
//方案二在代码加载时候进行判断,让addEvent返回正确的逻辑判断,但依然有个缺点,若代码从头到尾没有调用addEvent函数,则前一次的浏览器嗅探就是多余操作,且延长了ready加载时间
//惰性载入函数方案,addEvent依然被声明为普通函数,在函数中依然有判断分支,但在第一次进入分支后,在函数内部重写这个函数,重写之后的就是我们期望的函数,在下次进入的时候,函数里便不再存在条件分支语句。
var addEvent = function(element, type, handler) {
if (window.addEventListener) {
addEvent = function(element, type, handler) {
element.addEventListener(type, handler, false);
}
} else if (window.attachEvent) {
addEvent = function(element, type, handler) {
element.attachEvent('on' + type, handler);
}
}
addEvent(element, type, handler);
}