资讯专栏INFORMATION COLUMN

听飞狐聊JavaScript设计模式系列03

levy9527 / 3339人阅读

摘要:闭包与柯里化闭包有权访问另一个函数作用域中变量的函数。柯里化把接受多个参数的函数变换成接受一个单一参数最初函数的第一个参数的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

本回内容介绍

上一回聊到JS的Object类型,简单模拟了一下Java的Map,介一讲,偶们来聊一下函数好唔好,介可系JS世界的一等公民哟。从函数开始,我们就将逐步过渡到设计模式,来吧,帅狐带你装逼带你飞:

1. 函数的定义方式有三种

(1) 函数声明:

function o(a,b){
    return a+b;
}

(2) 函数表达式:

var o = function(a,b){
    return a+b;
};

(3) 构造函数式:

var o = new Function("a","b","return a+b");

这里简单的过一下函数定义,其他函数相关的基础知识还是那句:请参阅书籍或其他资料。

2. call, apply, bind

这仨都可以改变函数内部作用域的指向,bind()是ES5的新玩意儿,IE9以下不支持。call()和apply()一块儿说,这俩基友仅仅是传参不一样而已,apply传递的是数组:

function add(arg1,arg2) {
    return arg1+arg2;
};

function subtraction(arg1,arg2) {
    return arg1-arg2;
};
    
function optCall(arg1,arg2){
    return add.call(this,arg1,arg2);
};
    
function optApply(arg1,arg2){
    return subtraction.apply(this,[arg1,arg2]);
};
    
alert(optCall(1,2));    // 3
alert(optApply(2,1)); // 1

再来说说bind(),在高程3(JavaScript高级程序设计第三版)书上的说法是:bind()常常和回调函数与事件处理程序一起使用以便在将函数作为变量传递的同时保留代码执行环境。后面的设计模式讲解中我们会有事件的讲解,这里我们用书上的例子来模拟bind()的实现:

function bind(fn,context){
    // 这里是一个闭包
    return function(){
        // 这里调用传递的函数,并将参数传入
        return fn.apply(context,arguments);
    };
} 

这里光看例子可能有点抽象,没关系,后面讲设计模式,聊到观察者模式的时候还会聊到事件。

3. 闭包与柯里化

闭包:有权访问另一个函数作用域中变量的函数。这是书上的解释,有点懵吧,说白了,就是方法里面的方法就叫闭包。
柯里化:把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

之所以把这俩放在一起来聊,是因为这俩都不好理解,有点绕,而且柯里化依赖于闭包来实现,来吧,直接看书上的例子:

var c = console;
function curry(fn){
    // 这个地方是获取Array原型的slice方法,之所以这么用,是因为数组未被实例化之前,无法获得原型上的方法,可能有点绕,先不管原型后面会讲
    var _slice = Array.prototype.slice;        
    // 这里获取的是传入的第二个参数,也就是5
    var args = _slice.call(arguments,1);    
    c.log(args);    // [5]
    // 这里是一个闭包,这个函数的作用是组合外部传参和内部传参                        
    return function(){
        // 在这里传入的arguments是[15,10]
        var innerArgs = _slice.call(arguments);
        c.log(innerArgs);    // [15,10]    
        var finalArgs = args.concat(innerArgs);    // 内外部数组合并
        c.log(finalArgs);    // [5,15,10]
        // 这里写null或是其他都可以,因为这里只是执行传入的方法,也就add()方法
        return fn.apply(null,finalArgs);
    };
}

function add(a,b,c){
    return a+b+c;
}
alert(curry(add,5)(15,10));

很多盆友说这个例子木有看懂,现在配上了注释,赶脚有木有好点。这里没有多带带聊闭包,因为网上关于闭包的资料已经很多了。

顺便在啰嗦一句,有哥们儿面试问到了,fn.apply(null,finalArgs)这里如果是null的话,指向的是什么?答案是Global,而在浏览器环境下,Global就是window。

又是装逼时刻了,好咯,开始敲代码了:

这一回讲的内容比较绕脑袋,其实所有的函数都是Function类型的实例。一时理解不了也没关系,先囫囵吞枣,后面的内容还会涉及,下面还是进入做题环节:

某客面试题,正好复习函数柯里化

据说这家公司笔试题有四道题,这里先聊两道题,另外了道题后面会聊到...

1.题目:five(one(one())) 返回 511
function one(){
    var tags = typeof arguments[0];    // 获取传入的第一个参数类型
    var arg = "";
    switch(tags){
        // 判断如果是undefined,说明是最里层的函数
        case "undefined":
            arg = 1+"";
            break;
        // 判断如果是string,说明已经执行过最里层函数了,也就是说,已经跑过case为"undefined"的情况了
        case "string":
            arg = 1+arguments[0];
            break;
    };
    return arg;
};
function five(){
    return 5+arguments[0];
};
alert(five(one(one())));
alert(five(one(one(one(one(one(one()))))))); 

这个例子并非柯里化的函数,因为每一次one()的返回都是一个字符串,但是对比函数柯里化,使函数柯里化的概念更直观了。

我们把这个five()改改,让他变为柯里化,让帅狐show给你看:

function five(){
    var args = arguments[0];
    // 这里就是一个闭包的体现,然后自执行,返回的依然是字符串
    return (function(){
        return 5+args;
    })();
}; 

怎么样,帅吧!好吧,如果不帅,继续下一题,走你:

2. 题目:数组去重、并按倒数第二个字母排序
function unique(arr) {
    // res是结果数组,o是一个对象,上一回已经讲过了,对象的key是不可重复的
    var res = [], o = {};
    // 遍历传入的数组
    for (var i=0; arr[i]!=null; i++) {
        // 这里把数组的值作为对象的key进行判断,如果不存在则放入
        if (!o[arr[i]]) {
            // 这步是取过滤后的每一项放入结果数组
            res.push(arr[i]);
            // 这步把传入值作为对象key,并且赋值
            o[arr[i]] = true;
        }
    }
    return res;    // 返回结果数组,
}
    
alert(unique(["帅狐","帅狐",9,4,9,4,"帅","帅"]));    // 帅狐,9,4,帅 

这样就完成了数组去重,下一步,按照倒数第二个字母排序:

var arr =["javaScript","java","mongo","mysql", "css","html","nodejs","php"];
    
function compare(pre,cur){
    var p = pre.charAt(pre.length-2);    // 取倒数第二个字母
    var c = cur.charAt(cur.length-2);
    if(p < c){ 
        return -1; 
    }else if(p > c){ 
        return 1; 
    }else{ 
        return 0; 
    }
}
    
alert(arr.sort(compare));
// 返回["mongo", "php", "nodejs", "html", "javaScript", "mysql", "css", "java"]

之所以多带带写第二步,因为sort()排序是按照字符编码的顺序进行排序,在传入数值的时候是有陷阱的,不会对数值大小进行排序,看例子:

var arr =[1,10,2,99,3,200];
    
function sortNum(a,b){
    return a - b
}
    
alert(arr.sort());    // 返回[1, 10, 2, 200, 3, 99]
alert(arr.sort(sortNum));    // 返回[1, 2, 3, 10, 99, 200] 

这一回,主要过了一下Function类型,聊了一些函数的技巧,做了两道题,难度适中。
下一回,咱主要聊一聊,类的模拟,原型,继承,包括浅聊一下工厂模式,继续装逼继续飞。
话说最近港囧和夏洛特烦恼惹火了很多老歌,对于喜欢听老歌的我真的是大爱吖,dilililidilililidada...dilililidilililidada...

注:此系飞狐原创,转载请注明出处

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86118.html

相关文章

  • 飞狐JavaScript设计模式系列06

    本回内容介绍 上一回聊到JS中模拟接口,装饰者模式,掺元类,分析了backbone的继承源码,感觉还好吧! 介一回,偶们来聊一下在JS单例模式(singleton),单例模式其实运用很广泛,比如:jquery,AngularJS,underscore吖虾米的都是单例模式,来吧,直接开始咯: 1. 单例模式 保证一个类只有一个实例,从全局命名空间里提供一个唯一的访问点来访问该对象。其实之前写过的对象...

    hiYoHoo 评论0 收藏0
  • 飞狐JavaScript设计模式系列02

    摘要:本回内容介绍上一回聊到数据类型,简单的过了一遍,包括个数组新特性等,这一回来聊聊对象,结合数组来实战一些例子,在做题中成长,记忆会更深刻,来吧,开始咯创建实例的方式有两种使用操作符后跟构造函数飞狐使用对象字面量表示法飞狐也可以飞狐这种写法与 本回内容介绍 上一回聊到JS数据类型,简单的过了一遍,包括9个数组新特性等,这一回来聊聊Object对象,结合数组来实战一些例子,在做题中成长,记...

    tangr206 评论0 收藏0
  • 飞狐JavaScript设计模式系列11

    摘要:桥接模式之特权函数特权函数,用一些具有特权的方法作为桥梁以便访问私有空间,可以回忆一下之前的系列。连续自然数分组,计算最多组的个数将至这个连续自然数分成组使每组相加的值相等。个数组中数字最多的一组有个此时的和为。 本回内容介绍 上一回,聊了适配器模式,图片预加载,介一回,聊桥接模式(Bridge),跟之前一样,难度比较小,桥接模式将抽象部分与它的实现部分分离,通过桥接模式联系彼此,同时...

    wanglu1209 评论0 收藏0
  • 飞狐JavaScript设计模式系列10

    摘要:本回内容介绍上一回,聊了代理模式,虚拟代理,图片懒加载,介一回,也比较容易,适配器模式,用一个新的接口对现有类的接口进行包装,处理类与的不匹配。这一回,主要聊了适配器模式,图片预加载,主要还是理解下一回,聊一聊桥接模式,顺便做一做计算题。 本回内容介绍 上一回,聊了代理模式,虚拟代理,图片懒加载,介一回,也比较容易,适配器模式(Adapter),用一个新的接口对现有类的接口进行包装,处...

    yexiaobai 评论0 收藏0
  • 飞狐JavaScript设计模式系列14

    摘要:本回内容介绍上一回,聊了聊状态模式,并介绍了一下介一回,聊链式编程,模拟一下,再模拟一下封装一个库。这一回,主要聊了链式调用,模拟了,尤其是,希望大家能喜欢这次代码分享。下一回,聊一聊的策略模式。 本回内容介绍 上一回,聊了聊状态模式(State),并介绍了一下vue.js;介一回,聊链式编程,模拟一下jQuery,再模拟一下underscore.js,封装一个库。 1. 链式调用 (...

    fox_soyoung 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<