zoukankan      html  css  js  c++  java
  • js科里化

    科里化定义如下: 首先将一批函数转入一个函数(然后这个函数返回一个新的函数),这中形式就叫“做科里化”(currying)

    Function.prototype.curry = function(){
        var fn = this, // 这里就是在预装参数,将参数抓住,缓存在变量args中
        args = Array.prototype.slice.call(arguments);
        return function(){
            return fn.apply(this, args.concat(
                Array.prototype.slice.call(arguments)))
        };
    };
    
    String.prototype.csv = String.prototype.split.curry(/,s*/);
    var results = ("Mugan, Jin, Fuu").csv();
    
    console.log(results[0],results[1],results[2]);

    上面这个例子看懂了吗?

    curry这个函数做的事情,是将函数中的this和arguments缓存在了闭包之中。

    当split函数调用curry的时候,curry中的this就是split函数本身, 正则表达式这个参数则是预存在curyry中的arguments。

    虽然这段代码实现已经很不错了,但是我们还可以进行一些改进。 上面这个curry函数,实现了将所有参数缓存在闭包之中。 当调用闭包返回的新的函数的时候才去处理之前预装的所有参数。

    但是如果我不想让所有的参数全部预装,而是只预装其中一部分, 另外一部分参数要在调用闭包返回的那个新的函数中,将这部分参数传入。

    Function.prototype.partial = function(){
        var fn = this, args = Array.prototype.slice.call(arguments);
        return function(){
            var arg = 0;
            for (var i = 0; i < args.length && arg < arguments.length; i++){
                if (args[i] === undefined){
                    args[i] = arguments[arg++];
                }
            }
            return fn.apply(this, args);
        };
    };
        
    
    var delay = setTimeout.partial(undefined, 10);
    delay(function(){
        alert(true);
    });
    
    var bindClick = document.body.addEventListener.partial("click", undefined, false);
    bindClick(function(){
        alert(true);
    });

    partial()的实现和curry()有些相像。 在第一批的参数中,我们用undefined代表了那部分缺省参数。 在后面delay调用的时候,才将真正想要被处理的参数传递进去。

    再来看一道面试题:

    var fn = function(a,b,c){
        return a+b+c;
    }

    需要写一个函数,满足curry(fn)(1)(2)(3) //6

    var curry = function(fn){
        //参数集合
        var args = [];
        //函数的形参个数
        var fnLen = fn.length;
        
        return function(){
            //合并参数
            args = args.concat([].slice.call(arguments));
            //返回函数自身
            if(args.length < fnLen) return arguments.callee;
            //执行函数并返回结果
            return fn.apply(this,args);
        };
    };
    
    
    var fn=function(a,b,c){
        return a+b+c;
    };
    
    var c = curry(fn)(1)(2)(3);
    alert(c); //6

    用AOP装饰函数

    首先给出Function.prototype.before方法和Function.prototype.after方法:

    Function.prototype.before = function(beforefn){
        var _self = this;   //保存原函数的引用
        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;
        }
    };

    Function.prototype.before接受一个函数当作参数,这个函数即为新添加的函数,它装载了新添加的功能代码。

    接下 来把当前的this保存起来,这个this指向原函数,然后返回一个“代理”函数,这个“代理”函数只是结构上像代理而已,并不承担代理的职责(比如控制 对象的访问等)。它的工作是把请求分别转发给新添加的函数和原函数,且负责保证它们的执行顺序,让新添加的函数在原函数之前执行(前置装饰),这样就实现 了动态装饰的效果。

    我们注意到,通过Function.prototype.after的原理跟Function.prototype.before一模一样,唯一不同的地方在于让新添加的函数在原函数执行之后再执行。

    用Function.prototype.before来增加新的window.onload事件是多么简单:

    window.onload = function(){
        console.log(1);
    }
       
    window.onload = (window.onload || function(){}).after(function(){
        console.log(2);
    }).after(function(){
        console.log(3);
    }).after(function(){
        console.log(4);
    });    
  • 相关阅读:
    [学习日记]进程、线程和模块
    [学习日记]对SOAP头内添加信息的验证,可实现对请求WEB服务进行身份验证。
    [梦里原创]关于猫和老鼠的问题(程序算法)
    [学习日记]对控件的继承和重载
    [学习日记]VB图像处理之像素的获取和输出
    猫和老鼠问题的讨论
    [音乐欣赏]丁香花
    推荐一个WINDOWS系统文件介绍的网站
    [转]查查在中国有多少人的名字和你一样!
    计算机语言发展图解
  • 原文地址:https://www.cnblogs.com/gongshunkai/p/6653458.html
Copyright © 2011-2022 走看看