3.2高阶函数

2019-02-07  本文已影响0人  翊只仙女

资料整理:JavaScript设计模式与开发实践

高阶函数:函数可以作为参数被传递或作为返回值输出。

函数作为参数传递
  1. 回调函数
  2. Array.prototype.sort

sort()方法用原地算法对数组进行排序,并返回数组,默认排序是根据字符Unicode码点。sort()方法接受一个函数作为参数,这个函数封装了元素的排序规则。

函数作为返回值输出

让函数返回一个可执行的函数,意味着运算过程是可延续的。

  1. 判断数据类型
  2. 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();

高阶函数的其他运用

  1. 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();
  1. 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);
  1. 函数节流
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 );
  1. 分时函数
    有些函数确实是用户主动调用的,但因为一些客观原因却会严重影响页面性能,例如创建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();
  1. 惰性加载函数
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);
        }
上一篇 下一篇

猜你喜欢

热点阅读