前端系统化学习【JS篇】:(十三)JS高阶编程(单例设计模式、惰性思想、柯理化思想、compose函数)

1,248 阅读7分钟

前言

  • 细阅此文章大概需要 10分钟\color{red}{10分钟}左右
  • 本篇中讲述了:
      1. 单例设计模式
      1. 高级单例设计模式【基于闭包管控的单例模式】
      1. 惰性思想【函数重构】
      1. 柯理化函数思想【预处理】
          1. 回调函数
          1. 函数式编程
          1. 命令式编程
      1. compose函数【聚合/组合函数扁平化】
  • 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!
  • 欢迎转载,注明出处即可。

JS高阶编程

JS的单例设计模式

  • 【用单独的实例,来管理当前事物的相关特征】,类似于实现一个【分组】的特点,而此时实例名不仅仅叫做一个对象,也被称为命名空间,而描述当前事物的各种不同属性或方法都存放在这同一个命名空间中,两个命名空间之间相同属性不会冲突
    • 【A和B是两个人,虽然他们都是人类,但是都有各自的姓名和年龄,而且他们各自有各自的特点。假如两人重名,也并不会有冲突存在,依然是各自独立的个体】
    // 这里OBJ1和OBJ2就是两个单独实例
    // 每一个obj都是当前OBJECT这个基类的实例,
    // 而OBJ1和OBJ2是两个单独实例,而【把每个实例看作【描述当前事物的各种不同属性或方法的一个集成】】
    // 这样虽然OBJ1和OBJ2都是同一个类的实例,但是所描述的事物的特征是各不相同也互不干扰的。
        let obj1 = {
        name:'xxx';
        age:25,
        say(){
            //ES6写法,属性是一个函数可直接写成这种格式
        }
    };

JS的高级单例设计模式【基于闭包管控的单例模式】

  • 高级单例设计模式,早期的模块划分就是以此来实现的
  • 在闭包当中的方法若想在单例外使用,则可以将其暴露在对象当中,当外部调用时直接通过对象调用即可,实现方法既可以私有也可以公有
    • 在闭包中把一些私有信息暴露给外部使用

      • return通过返回值赋值暴露出去
      • window.xxx = xxx通过将方法或变量赋值给全局对象作为属性,从而暴露出去
    • 【或暴露出去【本例中是将tools方法作为一个对象的一个属性】【而这个对象被赋值给了全局上下文中的module1】【从而将tools暴露给了外面,通过对象.属性的方式来调用】module1.tools】
    let module1 = (function (){
        function query(){};
        function tools(){};
        return{//返回一个对象
            name : 'AREA',
            tools//把命名空间中私有的方法暴露在对象中,以供模块【单例】外部调用,实现方法既可以私有也可以公有
        };
        window.query = query;//暴露query方法给全局
    })();

    //在闭包当中的方法若想在单例外使用,则可以将其暴露在对象当中
    //当外部调用时直接通过对象调用即可,实现方法既可以私有也可以公有
    //======================================
    let A = (function (){
        function query(){};
        function fn(){};
        return {
            query
        }
        //想要调用别的单例中的方法
        B.fn();
    })();
    let B = (function (){
        unction query(){};
        function fn(){};
        function getxxx(){};
        return {
            getxxx
        }
        //想要调用别的单例中的方法
        A.query();
    })();

JS的惰性思想:【也是闭包的应用】

  • 【懒】执行过一次的东西,如果执行第二次还是一样的效果,就避免他重复执行,浪费性能
  • 代码上的表现就是函数重构
//获得某一元素的样式【兼容全部浏览器的】【需要判断兼容】
//实现了只判断一次兼容性,避免了消耗性能【惰性思想】
    function getCss(elemrnt,attr){
        //在两种不同的情况下分别重构函数
        if('getComputedStyle' in window){//新版浏览器【IE6~8中不兼容,需要基于currentStyle来获取】
            getCss = function (element,attr){//形成闭包
                return window.getComputedStyle(element)[attr];
            };
        }else{//旧版浏览器
            getCss = function (element,attr){//形成闭包
                return window.getComputedStyle(element)[attr];
            };
        }
        //为了第一次获取也能获取到值
        return getCss(element,attr);
    }
    //=========
    //调用
    getCss(document.body,'margin');

JS的柯理化函数思想:【闭包的应用】

  • 利用闭包的保存机制,把一些信息预先储存下来(预处理思想)
    function fn(...outerArgs){//剩余运算符:把逗号隔开的值序列组合成一个数组
        return function anonymous(...innerArgs){
            //Args:外层和里层函数传递的所有值【数组】合并在一起
            let args = outerArgs.concat(innerArgs);
            return args.reduce((n,item) =>n+item);
            // 返回的是一个数组
        };
    }

    let f = fn(20,30,40);//通过闭包的保存机制,相当于预先把值都存储起来
    f(40);// 130 通过闭包的保存机制,相当于预先把值都存储起来
    f(100);// 190 通过闭包的保存机制,相当于预先把值都存储起来

    let res = fn(1,2)(3);
    console.log(res);//=>6
  • 回调函数:

    • 把一个函数作为值传给另一个函数,在另外一个函数中把这个函数执行(是实现函数式编程的重要知识)
  • 函数式编程:

    • 注重结果,不在乎过程,过程交给别人处理【把如何实现封装成方法,之后只要调取API【该方法】,即可获得想要的结果】【与命令式编程的不同就是把具体过程封装成了方法,体现了函数的封装性】
  • 命令式编程:

    • 注重过程,需要自己去实现过程

JS的compose函数【聚合/组合函数】思想:【多层嵌套函数扁平化处理】

  • 把多层函数嵌套调用扁平化
  • 实现一个compose函数
    const fn1 = x => x + 10;
    const fn2 = x => x - 10;
    const fn3 = x => x * 10;
    const fn4 = x => x / 10;
    let res = fn4(fn2(fn3(fn1(20))));//多层嵌套调用
    console.log(res);
//===============
//实现compose   =
//===============

function compose(...funcs){
    //FUNCS:存储按照顺序执行的函数(数组)=>[fn1,fn3,fn2,fn4]
    // compose执行,要想让返回结果紧接着能执行,那一定是返回了一个匿名函数【柯理化思想】
    return function anonymous(...args){
        //ARGS:存储第一个函数执行需要传递的实参信息(是一个数组)【当前例子中是20】
        if(funcs.length===0)return args;//判断一下如果没传函数进来返回什么
        if(funcs.length===1return funcs[0](...args);//判断只传进来一个函数返回什么
        //剩下就是每个函数运行完,如何把返回值作为参数传给下个函数【reduce】
        return funcs.reduce((n,item)=>{
            //第一次n的值:【第一个函数执行的实参】【20】 item是funcs数组的第一个要执行的函数
            //第二次n的值:上一次item函数执行的返回值作为实参传递给下一个要执行的函数 item是funcs数组中下一个要执行的函数
            Array.isArray(n)?item(...n):item(n);//判断某函数执行的返回值是否为数组,进而选择是否结构赋值

        },args);//传了reduce的第二个参数,则reduce里面的回调函数的第一个参数的值就改为他【此处args数组只有一个值20】
    }
}
let res1 = compose(fn1,fn3,fn2,fn4)(20);
console.log(res1);
//redux源码中的compose函数用的是另外的思想实现的【更复杂】