Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >深入理解JavaScript系列(49):Function模式(上篇)

深入理解JavaScript系列(49):Function模式(上篇)

作者头像
用户4962466
修改于 2019-12-16 03:17:23
修改于 2019-12-16 03:17:23
35800
代码可运行
举报
文章被收录于专栏:我的前端路我的前端路
运行总次数:0
代码可运行

本篇主要是介绍Function方面使用的一些技巧(上篇),利用Function特性可以编写出很多非常有意思的代码,本篇主要包括:回调模式、配置对象、返回函数、分布程序、柯里化(Currying)。

回调函数

在JavaScript中,当一个函数A作为另外一个函数B的其中一个参数时,则函数A称为回调函数,即A可以在函数B的周期内执行(开始、中间、结束时均可)。

举例来说,有一个函数用于生成node

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var complexComputation = function () { /* 内部处理,并返回一个node*/};

复制代码

有一个findNodes函数声明用于查找所有的节点,然后通过callback回调进行执行代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var findNodes = function (callback) {
var nodes = [];
var node = complexComputation();

// 如果回调函数可用,则执行它
if (typeof callback === "function") {
    callback(node);
}

nodes.push(node);
    return nodes;
};

复制代码

关于callback的定义,我们可以事先定义好来用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义callback
var hide = function (node) {
    node.style.display = "none";
};

// 查找node,然后隐藏所有的node
var hiddenNodes = findNodes(hide);

复制代码

也可以直接在调用的时候使用匿名定义,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 使用匿名函数定义callback
var blockNodes = findNodes(function (node) {
    node.style.display = 'block';
});

复制代码

我们平时用的最多的,估计就数jQuery的ajax方法的调用了,通过在done/faild上定义callback,以便在ajax调用成功或者失败的时候做进一步处理,代码如下(本代码基于jquery1.8版):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var menuId = $("ul.nav").first().attr("id");
var request = $.ajax({
  url: "script.php",
  type: "POST",
  data: {id : menuId},
  dataType: "html"
});

//调用成功时的回调处理
request.done(function(msg) {
  $("#log").html( msg );
});

//调用失败时的回调处理
request.fail(function(jqXHR, textStatus) {
  alert( "Request failed: " + textStatus );
});

复制代码

配置对象

如果一个函数(或方法)的参数只有一个参数,并且参数为对象字面量,我们则称这种模式为配置对象模式。例如,如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var conf = {
    username:"shichuan",
    first:"Chuan",
    last:"Shi"
};
addPerson(conf);

复制代码

则在addPerson内部,就可以随意使用conf的值了,一般用于初始化工作,例如jquery里的ajaxSetup也就是这种方式来实现的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 事先设置好初始值
$.ajaxSetup({
   url: "/xmlhttp/",
   global: false,
   type: "POST"
});

// 然后再调用
$.ajax({ data: myData });

复制代码

另外,很多jquery的插件也有这种形式的传参,只不过也可以不传,不传的时候则就使用默认值了。

返回函数

返回函数,则是指在一个函数的返回值为另外一个函数,或者根据特定的条件灵活创建的新函数,示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var setup = function () {
    console.log(1);
    return function () {
        console.log(2);
    };
};

// 调用setup 函数
var my = setup(); // 输出 1
my(); // 输出 2
// 或者直接调用也可
setup()();

复制代码

或者你可以利用闭包的特性,在setup函数里记录一个私有的计数器数字,通过每次调用来增加计数器,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var setup = function () {
    var count = 0;
    return function () {
        return ++count;
    };
};

// 用法
var next = setup();
next(); // 返回 1
next(); // 返回 2
next(); // 返回 3

复制代码

偏应用

这里的偏应用,其实是将参数的传入工作分开进行,在有的时候一系列的操作可能会有某一个或几个参数始终完全一样,那么我们就可以先定义一个偏函数,然后再去执行这个函数(执行时传入剩余的不同参数)。

举个例子,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var partialAny = (function (aps) {

    // 该函数是你们自执行函数表达式的结果,并且赋值给了partialAny变量
    function func(fn) {
        var argsOrig = aps.call(arguments, 1);
        return function () {
            var args = [],
                argsPartial = aps.call(arguments),
                i = 0;

            // 变量所有的原始参数集,
            // 如果参数是partialAny._ 占位符,则使用下一个函数参数对应的值
            // 否则使用原始参数里的值
            for (; i < argsOrig.length; i++) {
                args[i] = argsOrig[i] === func._
                            ? argsPartial.shift()
                            : argsOrig[i];
            }

            // 如果有任何多余的参数,则添加到尾部
            return fn.apply(this, args.concat(argsPartial));
        };
    }

    // 用于占位符设置
    func._ = {};

    return func;
})(Array.prototype.slice);

复制代码

使用方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义处理函数
function hex(r, g, b) {
    return '#' + r + g + b;
}

//定义偏函数, 将hex的第一个参数r作为不变的参数值ff
var redMax = partialAny(hex, 'ff', partialAny._, partialAny._);

// 新函数redMax的调用方式如下,只需要传入2个参数了:
console.log(redMax('11', '22')); // "#ff1122"

复制代码

如果觉得partialAny._太长,可以用__代替哦。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var __ = partialAny._;

var greenMax = partialAny(hex, __, 'ff');
console.log(greenMax('33', '44'));

var blueMax = partialAny(hex, __, __, 'ff');
console.log(blueMax('55', '66'));

var magentaMax = partialAny(hex, 'ff', __, 'ff');
console.log(magentaMax('77')); 

复制代码

这样使用,就简洁多了吧。

Currying

Currying是函数式编程的一个特性,将多个参数的处理转化成单个参数的处理,类似链式调用。

举一个简单的add函数的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function add(x, y) {
    var oldx = x, oldy = y;
    if (typeof oldy === "undefined") { // partial
        return function (newy) {
            return oldx + newy;
        }
    }
    return x + y;
}

复制代码

这样调用方式就可以有多种了,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 测试
typeof add(5); // "function"
add(3)(4); // 7

// 也可以这样调用
var add2000 = add(2000);
add2000(10); // 2010

复制代码

接下来,我们来定义一个比较通用的currying函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 第一个参数为要应用的function,第二个参数是需要传入的最少参数个数
function curry(func, minArgs) {
    if (minArgs == undefined) {
        minArgs = 1;
    }

    function funcWithArgsFrozen(frozenargs) {
        return function () {
            // 优化处理,如果调用时没有参数,返回该函数本身
            var args = Array.prototype.slice.call(arguments);
            var newArgs = frozenargs.concat(args);
            if (newArgs.length >= minArgs) {
                return func.apply(this, newArgs);
            } else {
                return funcWithArgsFrozen(newArgs);
            }
        };
    }

    return funcWithArgsFrozen([]);
}

复制代码

这样,我们就可以随意定义我们的业务行为了,比如定义加法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var plus = curry(function () {
    var result = 0;
    for (var i = 0; i < arguments.length; ++i) {
        result += arguments[i];
    }
    return result;
}, 2);

复制代码

使用方式,真实多种多样哇。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
plus(3, 2) // 正常调用
plus(3) // 偏应用,返回一个函数(返回值为3+参数值)
plus(3)(2) // 完整应用(返回5)
plus()(3)()()(2) // 返回 5
plus(3, 2, 4, 5) // 可以接收多个参数
plus(3)(2, 3, 5) // 同理

复制代码

如下是减法的例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var minus = curry(function (x) {
    var result = x;
    for (var i = 1; i < arguments.length; ++i) {
        result -= arguments[i];
    }
    return result;
}, 2);

复制代码

或者如果你想交换参数的顺序,你可以这样定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var flip = curry(function (func) {
    return curry(function (a, b) {
        return func(b, a);
    }, 2);
});

复制代码

更多资料,可以参考如下地址:

http://www.caishui114.com/chanpin/ http://www.caishui114.com/wentiku/

总结

JavaScript里的Function有很多特殊的功效,可以利用闭包以及arguments参数特性实现很多不同的技巧,下一篇我们将继续介绍利用Function进行初始化的技巧。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JavaScript模式 读书笔记三
函数是第一类对象 first-class object,可以作为带有属性和方法的值以及参数进行传递。
lilugirl
2019/05/28
3260
深入理解JavaScript系列(50):Function模式(下篇)
本篇我们介绍的一些模式称为初始化模式和性能模式,主要是用在初始化以及提高性能方面,一些模式之前已经提到过,这里只是做一下总结。
用户4962466
2019/12/16
2990
深入理解JavaScript系列(31):设计模式之代理模式
代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西。
用户4962466
2020/01/15
4040
深入理解JavaScript系列(45):代码复用模式(避免篇)
任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题;第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题。
用户4962466
2019/12/19
3090
JavaScript 模式》读书笔记(4)— 函数2
  请注意introduceBugs()作为参数传递给writeCode()时是不带括号的。括号表示要执行函数,而在这种情况下,我们仅需要传递该函数的应用,而让writeCode()在适当的时候来执行它(也就是说,返回以后调用)。
zaking
2020/03/27
3750
深入理解JavaScript系列(44):设计模式之桥接模式
上述代码,有个问题就是getBeerById必须要有浏览器的上下文才能使用,因为其内部使用了this.id这个属性,如果没用上下文,那就歇菜了。所以说一般稍微有经验的程序员都会将程序改造成如下形式:
用户4962466
2019/12/19
4180
JavaScript高阶函数
把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等 可以保持业务逻辑模块的纯净和高内聚性
薛定喵君
2019/11/06
4590
深入理解JavaScript系列(32):设计模式之观察者模式
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
用户4962466
2020/01/07
4500
前端必知道的几个 JavaScript 高级函数
高阶函数是对其他函数进行操作的函数,可以将它们作为参数或通过返回它们。简单来说,高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回。
夜尽天明
2023/03/15
3940
前端必知道的几个 JavaScript 高级函数
2022秋招前端面试题(四)(附答案)
这里可以理解为await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中。
helloworld1024
2022/08/07
7260
深入理解JavaScript系列(48):对象创建模式(下篇)
本篇主要是介绍创建对象方面的模式的下篇,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码。
用户4962466
2019/12/16
3030
JavaScript专题之函数柯里化[通俗易懂]
在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
全栈程序员站长
2022/07/20
5430
深入理解JavaScript系列(46):代码复用模式(推荐篇)
同时,ECMAScript5也提供了类似的一个方法叫做Object.create用于继承对象,用法如下:
用户4962466
2019/12/19
3380
美团前端二面必会手写面试题汇总
观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观察者
helloworld1024
2022/10/10
3440
函数curry化(Haskell Curry)
当一个函数fn有多个参数时,可以先传入一部分参数,生成一个中继函数nextFn,然后在nextFn当中再传入剩下的参数。(一步curry化)
elson
2020/01/02
1.3K0
深入理解JavaScript函数式编程
什么是函数式编程(Functional Programming, FP):FP 是编程范式之一.(还有面向过程编程、面向对象编程)
用户3045442
2020/07/31
4.3K0
深入理解JavaScript函数式编程
前端经典面试题解密-add(1)(2)(3)(4) == 10到底是个啥?
前端的小伙伴在面试的时候,几乎都会遇到一道这样的面试题:add(1)(2)(3)(4)输出结果为10。在第一次看到这道面试题的时候,很多小伙伴感到了迷茫!借用王宝强在《人在囧途》中的表演:啥啥啥,这写的都是啥?下面胡哥为各位小伙伴带来这道题的揭秘。
胡哥有话说
2020/04/14
7420
JavaScript 语言精粹笔记1-语法、对象、函数
这里说一下JavaScript的注释,一种是 /* */ 包围的块注释,另一种是 // 开头的行注释。
零式的天空
2022/03/02
4200
JavaScript 语言精粹笔记1-语法、对象、函数
滴滴前端一面高频手写面试题汇总_2023-02-28
浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。
用户10358576
2023/02/28
7360
javascript中function用法_年终总结反思不足之处
整理了JavaScript中函数Function的各种,感觉函数就是一大对象啊,各种知识点都能牵扯进来,不单单是 Function 这个本身原生的引用类型的各种用法,还包含执行环境,作用域,闭包,上下文,私有变量等知识点的深入理解。
全栈程序员站长
2022/09/20
5090
推荐阅读
相关推荐
JavaScript模式 读书笔记三
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验