欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

[Node.js] 闭包和高阶函数

程序员文章站 2024-01-14 08:40:34
引子 最近发现一个问题:一部分写JS的人,其实对于函数式编程的概念并不是太了解。如下的代码片断,常常让他们觉得不可思议:     OAuth2Serve...
引子

最近发现一个问题:一部分写JS的人,其实对于函数式编程的概念并不是太了解。如下的代码片断,常常让他们觉得不可思议:

 

 

OAuth2Server.prototype.authCodeGrant = function (check) {

  var self = this;

 

  return function (req, res, next) {

    new AuthCodeGrant(self, req, res, next, check);

  };

};

 

上述片断来自开源项目node-oauth2-server,这个authCodeGrant原型函数涉及到JS编程中经常用到的两个概念:闭包 和 高阶函数(check变量在这个函数中被闭包,authCodeGrant能返回函数,因此是一个高阶函数。

 

闭包

闭包就是引用了*变量的函数。这个被引用的*变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

 

如何来理解这个*变量呢?

 

*变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量

 

什么样的变量是*变量呢?如下片断中的freeVar就是个*变量:

 

function wrapper() {

    var freeVar = 42;

    function inner() {

        return 2 * freeVar;

    }

    return inner;

 

*变量在闭包生成之前,并不是函数的一部分。在函数被调用时,闭包才会形成,函数将这个*变量纳入自己的作用域,也就是说,*变量从此与定义它的容器无关,以函数被调用那一刻为时间点,成为函数Context中的成员。

 

来看一个困惑前端的示例,循环添加事件:

 

    <button>第1条记录</button>

    <button>第2条记录</button>

    <button>第3条记录</button>

    <button>第4条记录</button>

    <button>第5条记录</button>

    <button>第6条记录</button>

<script type="text/javascript">  

    var buttonst_obj = document.getElementsByTagName("button"); 

    for (var i = 0, len = buttonst_obj.length; i < len; i++) {

        buttonst_obj[i].onclick = function() {   

            alert(i);   

        };

    }

</script>

 

上述片断的结果是:每个Button弹出的都是6。因为没有形成有效的闭包,因为闭包是有延迟求值特性的,所以在函数得到执行时,i === 6。

 

 

 

如果我们将它改成这样,i 做为外层函数的参数而被内层函数闭包,结果也是我们想要的:

 

var buttonst_obj = document.getElementsByTagName("button");

for (var i = 0, len = buttonst_obj.length; i < len; i++) {

    buttonst_obj[i].onclick = clickEvent(i);

}

function clickEvent(i){

    return function () {

        console.log(i);

    }

}

 

Why? 因为这个clickEvent(i) 高阶函数,它将 i 作为*变量(注意:i 并不是内函数的参数,也不是内函数的一部分)传递,在 click 时闭包已经形成并被传递。

 

高阶函数

上述循环事件片断中的 clickEvent(i) 即为一个高阶函数。

 

高阶函数满足:要么接受一个或多个函数作为输入;要么输出一个函数

 

为什么会用到高阶函数?粗糙的说,就是为了闭包。

 

接受函数作为输入的高阶函数

 

这种高阶函数可作为一种模式的构造器,比如:我有快速排序/堆排序/希尔排序 等若干个排序函数,那么我只需要提供一个高阶函数,就能生成基于这若干种排序函数的排序器:

 

//排序器

var sortingGenerator = function(sortFunc){

    return function(args){

        var arguments = [].slice.call(args);

        return sortFunc(arguments);

    }

};

//引入排序算法

var heapSort = require('heapSort');

var heapSorter = sortingGenerator(heapSort);

//使用算法

heapSorter(4, 22, 44, 66, 77);

 

当然,其实这个高阶函数也输出了函数

 

输出函数的高阶函数

 

和上例一样,高阶函数输出一个函数也很好理解:先闭包*变量,根据它在将来调用时产生不一样的输出。

 

比如,我需要一个函数,既可以算平方,也可以算立方,最好什么方都能算,这时我就需要一个如下片断的高阶函数:

 

 

//计算m的N次方

var powerOfN = function(n){

    return function(m){

        var res = 1;

        for(var i = 0; i < n; ++i){

            res *= m;

        }

        return res;

    } ;

};

//按需生成

var powerOf2 = powerOfN(2);

var powerOf3 = powerOfN(3);

//调用传参

console.log(powerOf2(3));

console.log(powerOf3(2));