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

《1》首先是惰性函数javascript

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

看下面典型的例子:html

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

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;
    }
}
web

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

因而有了以下改变:数组

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:app

 

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

 

纯属我的学习参考资料

相关文章
相关标签/搜索