8

本回内容介绍

上一回聊到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...


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


飞狐
2.4k 声望1.4k 粉丝

专注AI量化,技术界最懂金融的CFA金融分析师