关于js的高级函数(惰性函数,函数柯里化,级联函数)

《1》首先是惰性函数

惰性载入表示函数执行的分支只会在函数第一次掉用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。

看下面典型的例子:

为了兼容各浏览器,对事件监听的的支持:

function addEvent (type, element, fun) {
    if (element.addEventListener) {
        element.addEventListener(type, fun, false);
    }
    else if(element.attachEvent){
        element.attachEvent('on' + type, fun);
    }
    else{
        element['on' + type] = fun;
    }
}

上面是注册函数监听的各浏览器兼容函数。由于,各浏览之间的差异,不得不在用的时候做能力检测。显然,单从功能上讲,已经做到了兼容各浏览器。但是,每次绑定监听,都会对能力做一次检测,这就没有必要了,真正的应用中,这显然是多余的,同一个应用环境中,只需要检测一次即可。

于是有了如下改变:

function addEvent (type, element, fun) {
    if (element.addEventListener) {
        addEvent = function (type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    }
    else if(element.attachEvent){
        addEvent = function (type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    }
    else{
        addEvent = function (type, element, fun) {
            element['on' + type] = fun;
        }
    }
    return addEvent(type, element, fun);
}

可以看出,第一次调用addEvent会对浏览器做能力检测,然后,重写了addEvent。下次再调用的时候,由于函数被重写,不会再做能力检测。

类似的例子还有:XMLHttRequest:

function createXHR() {
 4     var xhr;
 5     try{
 6         xhr = new XMLHttpRequest();
 7     }catch(e) {
 8         handleErr(e);
 9 
10         try {
11             xhr = new ActiveXObject("Msxml2.XMLHTTP");
12         }catch(e) {
13             try{
14                 xhr = new ActiveXObject("Microsoft.XMLHTTP");
15             }catch(e) {
16                 xhr = null;
17             }
18         }
19     }
20 
21     return xhr ;
22 }
23 
24 function handleErr(error) {
25     // 这一步在实战中很重要,因为catch会延长作用域链,所以是在全局作用域声明的e
26     // 这里我们把它赋给局部变量,则查找更快
27     var err = error;
28 
29     // do sth.
30 }

由于不同的浏览器的xmlhttpreque对象的不同形式,为了兼容同样要在使用ajax时判断,但同样这样的判断只需一次。

看惰性函数优化后:

function createXHR() {
 5     var xhr;
 6     if(typeof XMLHttpRequest != 'undefined') {
 7         xhr = new XMLHttpRequest();
 8         createXHR = function() {
 9             return new XMLHttpRequest();
10         }
11     }else {
12         try {
13             xhr = new ActiveXObject("Msxml2.XMLHTTP");
14             createXHR = function() {
15                 return new ActiveXObject("Msxml2.XMLHTTP");
16             }
17         }catch(e) {
18             try {
19                 xhr = new ActiveXObject("Microsoft.XMLHTTP");
20                 createXHR = function() {
21                     return new ActiveXObject("Microsoft.XMLHTTP");
22                 }
23             }catch(e) {
24                 createXHR = function () {
25                     return null;
26                 }
27             }
28         }
29     }
30     return xhr 
31 }

代码中,我们让函数在第一次运行之后,则判断除了浏览器的环境,就被重新赋值了。
赋值后的函数是直接return 对应的方法。所以,这个函数,需要第二次调用的时候才真正的被调用。
正是因为它第二次调用函数的时候,没有去走第一次调用那样复杂的判断的路,所以显得“懒惰”。因此我们叫它 惰性函数

可以总结出在哪些情况下使用惰性函数:

1 应用频繁,如果只用一次,是体现不出它的优点出来的,用的次数越多,越能体现这种模式的优势所在;


2 固定不变,一次判定,在固定的应用环境中不会发生改变;

《2》函数柯里化

关于这个看了好多资料,其实还是并没有很懂,<摊手>

直接上代码:

// //函数柯里化

1》提高适用性:

【通用函数】解决了兼容性问题,但同时也会再来,使用的不便利性,不同的应用场景往,要传递很多参数,以达到解决特定问题的目的。有时候应用中,同一种规则可能会反复使用,这就可能会造成代码的重复性。

function square(i) {
    return i * i;
}

function dubble(i) {
    return i *= 2;
}

function map(handeler, list) {
    return list.map(handeler);
}

// 数组的每一项平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]);
// ......

// 数组的每一项加倍
map(dubble, [1, 2, 3, 4, 5]);
map(dubble, [6, 7, 8, 9, 10]);
map(dubble, [10, 20, 30, 40, 50]);

函数柯里化改造后:

function curry(fn){
 var args=Array.prototype.slice.call(arguments,1);
 console.log(args);//输出:function aquare(){...}
 return function(){
  var innerArgs=Array.prototype.slice.call(arguments);
  console.log(innerArgs);//输出:1,2,3,4,5
  var finalArgs=args.concat(innerArgs);
  console.log(finalArgs);//输出:function square(){},1,2,3,4,5
  return  fn.apply(null,finalArgs);
 }
}

function square(i) {     return i * i; }

function dubble(i) {     return i *= 2; }

function map(handeler, list) {     return list.map(handeler); }

var mapSQ = curry(map, square);

mapSQ([1, 2, 3, 4, 5]);

mapSQ([3,2,5,7,6,7]);

var mapDB = curry(map, dubble);

mapDB([1, 2, 3, 4, 5]);

mapDB([2,5,7,6,2,3]);

柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。
因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。

var currying = function (fn) {
    var _args = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(this, _args);
        }
        Array.prototype.push.apply(_args, [].slice.call(arguments));
        return arguments.callee;
    }
};

var multi=function () {
    var total = 0;
    for (var i = 0, c; c = arguments[i++];) {
        total += c;
    }
    return total;
};

var sum = currying(multi); 
 
sum(100,200)(300);
sum(400);
console.log(sum());     // 1000  (空白调用时才真正计算)

上面的代码其实是一个高阶函数(high-order function), 高阶函数是指操作函数的函数,它接收一个或者多个函数作为参数,并返回一个新函数。此外,还依赖与闭包的特性,来保存中间过程中输入的参数。即:

  • 函数可以作为参数传递
  • 函数能够作为函数的返回值
  • 闭包

柯里化的作用

  • 延迟计算。上面的例子已经比较好低说明了。
  • 参数复用。当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。
  • 动态创建函数。这可以是在部分计算出结果后,在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算。或者可以通过将要传入调用函数的参数子集,部分应用到函数中,从而动态创造出一个新函数,这个新函数保存了重复传入的参数(以后不必每次都传)。例如,事件浏览器添加事件的辅助方法:

var addEvent = function(el, type, fn, capture) {
     if (window.addEventListener) {
         el.addEventListener(type, function(e) {
             fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
         el.attachEvent("on" + type, function(e) {
             fn.call(el, e);
         });
     }
};

每次添加事件处理都要执行一遍 if...else...,其实在一个浏览器中只要一次判定就可以了,把根据一次判定之后的结果动态生成新的函数,以后就不必重新计算。

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();

《3》级联函数

!--JavaScript级联函数-->
<!--本课时介绍JavaScript级联函数,
级联函数也叫链式函数,方法链一
般适合对一个对象进行连续操作
(集中在一句代码)。一定程度上
可以减少代码量,缺点是它占用了
函数的返回值。-->

function myclassA(){
 this.name="";
 this.age="";
 this.sex="";
}
myclassA.prototype={
 setname:function(){
  this.name="katherine";
  return this;
 },
 setage:function(){
  this.age="22";
  return this;
 },
 setsex:function(){
  this.sex='girl';
  return this;
 }
}
var me =new myclassA();
console.log(me.setname().setage().setsex());

// myclassA {name: "katherine", age: "22", sex: "girl"}

参考自http://www.tuicool.com/articles/N7Z3qey,

参考:

http://sombie.diandian.com/post/2013-06-28/40050585369

http://book.2cto.com/201211/9320.html

http://zh.wikipedia.org/wiki/Currying

http://www.ibm.com/developerworks/cn/web/1006_qiujt_jsfunctional/

http://www.cnblogs.com/lwbqqyumidi/archive/2012/12/03/2799833.html

纯属个人学习参考资料