• 提高你的javascript代码逼格系列之函数与数组


    不知道大家有没有一种感觉,那就是自己写的javascript代码虽然能完全解决工作上的需要,但是,一眼望去,too simple!!!简直就是一个傻子都能看懂的水平,于是,在工作之余,我开始去收集一些几乎在日常开发中未曾用过的javascript写法/有价值的javascript知识点,借此来提高自身javascript水平。

    一、函数式编程与面向对象编程

    <script>
        /*函数式编程*/
        function f1(){
        var n=999;
        nAdd=function(){
                n+=1;
            };
        var f2 = function(){
          console.log(n);
        };
        return f2;
      }
      var result=f1();
      result(); // 999
      nAdd();
      result(); // 1000
    
        /*面向对象编程1之json写法*/
        var obj1 = {
            n:999,
            nAdd:function(){
                this.n += 1;
            },
            getN:function(){
                console.log(this.n);
            }
        };
        obj1.getN();//999
        obj1.nAdd();
        obj1.getN();//1000
    
        /*面向对象编程2之工厂模式*/
        var obj2 = function(){
            var obj = new Object;
            obj.n = 999;
            obj.nAdd = function(){
                this.n += 1;
            };
            obj.getN = function(){
                console.log(this.n);
            }
            return obj;
        };
        var objins = new obj2();
        objins.getN();//999
        objins.nAdd();
        objins.getN();//1000
    
        /*面向对象编程3之原型prototype*/
        function obj3(n) {
                    this.n = n;
        }
        obj3.prototype.nAdd = function() {
            this.n+=1;
        };
        obj3.prototype.getN = function() {
            console.log(this.n);
        }
        var objins2 = new obj3(999);
        objins2.getN();//999
        objins2.nAdd();
        objins2.getN();//1000
    </script>
    View Code

    针对上面所示的函数式编程,涉及了作用域和闭包的知识,这里简要提一下;

    里面的nAdd没有用var关键字声明,所以他是全局的,后面可以全局调用,其次利用闭包访问和修改了函数作用域内的n值。

    二、纯函数和非纯函数

    1.此函数在相同的输入值时,总是产生相同的输出。函数的输出和当前运行环境的上下文状态无关。

    2.此函数运行过程不影响运行环境,也就是无副作用(如触发事件、发起http请求、打印/log等)。

    简单来说,也就是当一个函数的输出不受外部环境影响,同时也不影响外部环境时,该函数就是纯函数,也就是它只关注逻辑运算和数学运算,同一个输入总得到同一个输出。

    var xs = [1,2,3,4,5];
    // 纯的
    xs.slice(0,3);
    //=> [1,2,3]
    xs.slice(0,3);
    //=> [1,2,3]
    xs.slice(0,3);
    //=> [1,2,3]
    
    // 不纯的
    xs.splice(0,3);
    //=> [1,2,3]
    xs.splice(0,3);
    //=> [4,5]
    xs.splice(0,3);
    //=> []
    View Code

    纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。

    三、函数的柯里化

    curry 的概念很简单:将一个低阶函数转换为高阶函数的过程就叫柯里化。

    函数柯里化是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。从某种意义上来讲,这是一种对参数的缓存,是一种非常高效的编写函数的方法:

                    /*一般写法:两数相加*/
            var add = function(x,y){
                return x + y;
            }
            /*2+1和2+10*/
            add(2,1);//3
            add(2,10);//12
    
            /*柯里化写法*/
            //es5
            var add = function(x) {
              return function(y) {
                return x + y;
              };
            };
            //es6
            var add = x => (y => x + y);
    
            //定义+1和+10函数
            var increment = add(1);//柯里化后得到的+1函数
            var addTen = add(10);//柯里化后得到的+10函数
            increment(2);  // 3
            addTen(2);  // 12
    View Code

    四、Array.reduce/Array.reduceRight(数组迭代)

    先不看标题,来个题目:如何求数组[1,2,3,4,5]的各项累加的和?

    大多数人恐怕会立即说出for循环、while循环,的确,for循环和while循环是可以解决上述问题,但是,这个答案显然脱离了此篇博客的主题:逼格!!!

    并且,实验表明,将上述求和方法循环1W次,用for循环和while循环耗时大概在50~55ms,但是,用Array.reduce/Array.reduceRight仅仅耗时0.4ms!!!

    关于该方法的详解,请猛戳→https://www.w3cplus.com/javascript/array-part-8.html;这里给个demo:

    var arr = [1,2,3,4,5];
            /*ES6写法*/
            var reducer = (prevalue,curvalue)=>prevalue + curvalue;
            console.log(arr.reduce(reducer));//15
            /*ES5写法*/
            var reducer = function(prevalue,curvalue){
                return prevalue + curvalue;
            };
            console.log(arr.reduce(reducer));//15
            /*解析*/
            数组的reduce方法有两个参数:callback,prevalue;
            对于第一个参数callback,里面有4个参数prevalue、curvalue、index、array;
            index是当前迭代时的curvalue的索引(数组中的索引),array当然是操作的数组了;
            着重看另外两个参数,prevalue和curvalue(上一个值和当前值);
            如果reduce方法没有第二个参数的话,那么callback回调将数组第一个值作为prevalue,第二个值作为curvalue;
            如果reduce方法有第二个参数,那么将第二个参数值作为prevalue,将数组第一个值作为curvalue;
            每一次的操作结果将变为下一次操作的prevalue,而curvalue的值教上一次往后推一位;
            /*实例解析*/
            针对上述数组相加;
            没有第二个参数,只有一个回调callback,那么他的过程是这样的;
            第一次运算:prevalue为1,curvalue为2,运算结果为1+2=3,并将结果作为下次运算的prevalue;
            第二次运算:prevalue为3(上一次运算的结果),curvalue为3,运算结果为3+3=4,以此类推;
    
            如果有第二个参数,假设为5,arr.reduce(reducer,5),那么将是这么计算的;
            第一次运算:prevalue为5(第二个参数),curvalue为1,运算结果为5+1=6,并将结果作为下次运算的prevalue;
            第二次运算:prevalue为6(上一次运算的结果),curvalue为2,运算结果为6+2=8,以此类推;
    
            reduceRight和reduce类似,只不过他是从右边开始迭代的。
    View Code

     五、函数组合(reduce高级用法)

    假想一下,要将字符串“my name is echo”进行如下操作,你会怎么做?转大写、然后末尾加一个感叹号;

    我想你大概会定义一个函数operate,对于形参str,先toUpperCase(),然后+ '!',ok这样做没错,但是现在我需求改了,我不要求转大写了,你是不是得删除那行代码?

    可能你会说了,我定义两个函数一个是转大写,一个是加叹号,需要哪个用哪个,其实无论你用几个函数来写,还是直接用prototype或者链式调用的方法去写,都只是大众化的写法,且不易维护,so,请看下面的写法:

            var compose = (...args) => x => args.reduceRight((prevalue, curvalue) => curvalue(prevalue), x);
            var toUpperCase = x => x.toUpperCase();
            var exclaim = x => x + '!';
            var shout = compose(exclaim, toUpperCase);
            console.log(shout("my name is echo"));
    View Code

     一眼望去,是不是两眼懵逼?!!不怕,我们用ES5还原一下,请看:

            var compose = function (...args){
                return function (x){
                    return args.reduceRight(function(prevalue, curvalue){
                        return curvalue(prevalue);
                    },x)
                }
            };
            var toUpperCase = function(x){
                return x.toUpperCase();
            };
            var exclaim = function(x){
                return x + '!';
            };
            var shout = compose(exclaim, toUpperCase);
            console.log(shout("my name is echo"));
    View Code

    结合上述第四例所讲到的reduce,我希望此时你已经看懂了,没看懂也没关系,接着往下看,继续解析:

            var shout == compose(exclaim, toUpperCase)
                      == function (x) {
                          return [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){
                            return curvalue(prevalue);
                        },x)
                      };
            shout("my name is echo") == [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){
                            return curvalue(prevalue);
                        },"my name is echo");
            /*前面说过,如果reduce/reduceRight传了第二个参数,那么该第二个参数将作为prevalue给callback调用。*/
            /*运算过程如下(reduceRight从右往左)curvalue(prevalue):*/
            1.toUpperCase("my name is echo");并将结果作为prevalue供下次调用;
            2.exclaim(toUpperCase("my name is echo"));如此实现了先转大写再加感叹号。
    View Code
  • 相关阅读:
    【Web】JavaScript 语法入门
    tar 和gzip 的区别
    状态码,好记
    PyCharm与git/GitHub取消关联
    在Ubuntu下安装deb包需要使用dpkg命令
    linux每日命令(4):解压命令
    Python之os.path.join()
    Python的JAVA胶水——jpype
    python之chardet验证编码格式
    python之arrow时间处理模块
  • 原文地址:https://www.cnblogs.com/eco-just/p/10328522.html
Copyright © 2020-2023  润新知