深刻理解javascript系列(十六):深刻高阶函数

因为这两天,广州-东莞-惠州三日游,因此更新速度有所放慢...数组

前面咱们说过,简单点理解高阶函数,则凡是接收一个函数做为参数的函数,就是高阶函数...bash

大神说,高阶函数是一个高度封装的过程,理解它须要一点想象力。因此本次就借助几个例子,来理解高阶函数的封装。微信

1.  数组map方法封装的思考过程

咱们知道数组有一个map(映射)方法,它对数组中的每一项运行给定的函数,返回每次函数调用的结果并组成数组。简单来讲,就是遍历数组的每一项,而且在map的第一个参数中进行运算处理后返回计算结果,最终返回一个由全部计算结果组成的新数组。dom

//声明一个遍历的数据array
var array = [1,2,3,4];

//map方法第一个参数为一个回调函数,该函数拥有三个参数
//第一个参数表示array数组中的每一项
//第二个参数表示当前遍历的索引值
//第三个参数表示数组自己
//该函数中的this指向map方法第二个参数,若该参数不存在,则this指向丢失

var newArray = array.map(function(item, i, array) {
    console.log(item, i, array, this) //这个this是什么?
    return item + 1;
}, {a: 1})

//newArray为一个新数组,有map遍历的结果组成
console.log(newArray);复制代码

在上面的例子中,咱们详细分析了map的全部细节。如今须要思考的是,若是要咱们本身来封装这样一个方法,应该怎么办?模块化

由于全部的数组遍历方法,其实都是在for循环基础之上封装的,所以咱们能够从for循环开始考虑。函数

固然,一个for循环的过程其实很好封装,其难点在于,for循环里面对数组每一项所作的事情很难用一个固定的模式把他封装起来,在不一样的场景下,for循环对数据的处理确定是不同的。那么应该怎么办呢?学习

在封装函数时,对于一个不肯定的变量,咱们能够用往函数中传入参数的方式来指定。如:ui

function add(a) {
    return a + 10;
}复制代码

一样的道理,对于一个不肯定的处理过程,咱们能够用往函数中传入另一个函数的方式来自定义这个处理过程。所以,基于这个思路,咱们能够按照以下方式来封装map方法。this

Array.prototype._map = function(fn, context) {
    //首先定义一个数组来保存每一项的运算结果,最后返回
    var temp = [];
    if(typeof fn == 'function') {
        var k = 0;
        var len = this.length;
        //封装for循环过程
        for(; k<len; k++) {
            //将每一项的运算操做丢进fn里
            //利用call方法指定fn的this指向与具体参数
            temp.push(fn.call(context, this[k], k, this))
        }
    }else {
        console.error('TypeError: '+ fn +' is not a function.');
    }

    //返回每一项运算结果组成的新数组
    return temp;
}
 
var newArr = [1,2,3,4]._map(function(item) {
    return item + 1;
})复制代码

回过头反思map方法的封装过程能够发现,其实咱们封装的是一个数组的for循环过程。每个数组在使用for循环遍历时,虽然没法确认在for循环中到底发生了什么,可是能够肯定的是,它们必定会使用for循环。spa

所以咱们把“都会使用for循环”这个公共逻辑封装起来,而具体要作什么事,则以一个函数做为参数的形式,来让使用者自定义。这个被做为参数传入的函数,就能够称之为基础函数。而咱们封装的map方法,就能够称之为高阶函数。

高阶函数的使用思路正在于此,它实际上是一个封装公共逻辑的过程。

假设咱们正在作一个音乐社区的项目。

很显然,在进入这个项目的每个页面时,都必须判断当前用户是否已经登陆。由于登陆与未登陆所展现的页面确定是有不少差异的。不只如此,在确认用户登陆以后,还需获得用户的具体信息,如昵称、姓名、VIP等级、权限范围等。

所以用户状态的判断逻辑,是每一个页面都必需要作的一个公共逻辑,那么在学习了高阶函数以后,咱们就能够用高阶函数来作这件事。

为了强化模块化思惟,咱们继续使用模块化的方式来完成这个例子。在这里,咱们能够利用自执行函数来划分模块。

首先须要一个高阶函数来专门处理获取用户状态的逻辑,所以能够单独将这个高阶函数封装为一个独立的模块。

//高阶函数withLogin,用来判断当前用户状态
(function() {
    
    //用随机数的方式来模拟一个获取用户信息的方法
    var getLogin = function() {
        var a = parseInt(Math.random() * 10).toFixed(0));
        if(a % 2 == 0) {
            return {login: false}
        }
        
        return {
            login: true,
            userinfo: {
                nickname: 'pan',
                vip: 1,
                userid: 'music1111'
            }
        }
    }
    
    var withLogin = function(basicFn) {
        var loginInfo = getLogin();
        
        //将loginInfo以参数的形式传入基础函数中
        return basicFn.bind(null, loginInfo);
    }

    window.withLogin = withLogin;
})();复制代码

假设咱们要展现主页,则能够经过renderIndex的方法来渲染。固然,渲染主页仍然是一个单独的模块。

(function() {
    var withLogin = window.withLogin;

    var renderIndex = function(loginInfo) {
        //这里处理index页面的逻辑

        if(loginInfo.login) {
            //处理已经登陆以后的逻辑
        }else {
            //这里处理未登陆的逻辑
        }
    }
    
    //对外暴露接口时,使用高阶函数包一层,来判断当前页面的登陆状态
    window.renderIndex = withLogin(renderIndex);
})();复制代码

一样的道理,当咱们想要展现其它页面,例如我的主页时,则可使用renderPersonal方法,以下所示。

(function() {
    var withLogin = window.withLogin;
    var renderPersonal = function(loginInfo) {
        if(loginInfo.login) {
            //do something
        }else {
           // do other something
        }
    }
    
    window.renderPersonal = withLogin(renderPersonal);
})();复制代码

当咱们使用高阶函数封装每一个页面的公共逻辑以后,会发现咱们的代码逻辑变得很是清晰,并且更加统一。当再写新的页面逻辑时,就在此基础之上完成便可,而不用再去考虑已经封装过的逻辑。

最后,在合适的时机使用这些渲染函数便可。

(function() {
    window.renderIndex();
})();复制代码

这些都是我以往的学习笔记。若是您看到此笔记,但愿您能指出个人错误。有这么一个群,里面的小伙伴互相监督,坚持天天输出本身的学习心得,不输出就出局。但愿您能加入,咱们一块儿终身学习。欢迎添加个人我的微信号:Pan1005919589

相关文章
相关标签/搜索