《JavaScript 模式》读书笔记(4)— 函数2

这篇,我们仍旧继续学习函数。二、回调模式函数都是对象,这表示它们可以作为参数传递给其它函数。functionwriteCode(callback){//执行一些事务…callbac

大家好,又见面了,我是你们的朋友全栈君。

  这篇,我们仍旧继续学习函数。

 

二、回调模式

  函数都是对象,这表示它们可以作为参数传递给其它函数。

function writeCode(callback) {
    // 执行一些事务...
    callback();
    // ...
}
function introduceBugs() {
    // ...引入漏洞
}

writeCode(introduceBugs);

  请注意introduceBugs()作为参数传递给writeCode()时是不带括号的。括号表示要执行函数,而在这种情况下,我们仅需要传递该函数的应用,而让writeCode()在适当的时候来执行它(也就是说,返回以后调用)。

 

回调示例

  我们来看个例子,假设我们需要一个通用函数执行一些复杂逻辑后返回一个大块数据的结果。

var findNodes = function() {
    var i = 100000,// 大而繁重的循环
        nodes = [],// 存储结果
        found;// 找到了下一个节点
    while(i) {
        i -= 1;
        // 这里是复杂的逻辑...
        nodes.push(found);
    }
    return nodes;
}

  保持函数的通用性并且使其返回一个DOM节点数组,而不对实际元素做任何处理,这是一个非常好的思想。修改节点的逻辑,可以在其他函数中实现。

var hide = function (nodes){
    var i = 0,max = nodes.length;
    for(;i < max;i += 1) {
        nodes[i].style.display = "none";
    }
};

//执行该函数
hide(findNodes());

  上面的实现方式是低效的。因为hide()必须再次遍历由findNodes()返回的数组节点。如果能避免这种循环,并且只要在findNodes()中便可隐藏节点,那么这将是高效的实现方式。但是如果在findNodes()中实现隐藏逻辑,由于检索和修改逻辑耦合,那么它不再是一个通用函数。对这种问题的解决方法是采用回调模式,可以将节点隐藏逻辑以回调函数方式传递给findNodes()并委托其执行:

    // 重构findNodes()以接受一个回调函数
var findNodes = function(callback) {
    var i = 100000,// 大而繁重的循环
        nodes = [],// 存储结果
        found;// 找到了下一个节点

    // 检查回调函数是否为可调用的
    if(typeof callback !== "function") {
        callback = false;
    }

    while(i) {
        i -= 1;
        // 这里是复杂的逻辑...
        // 现在运行回调函数
        if(callback){
            callback(found);
        }
        nodes.push(found);
    }
    return nodes;
}

  这是一种很直接的实现方法。findNodes()执行的唯一额外任务是,检查是否提供了可选回调函数,如果存在就执行。其中,回调函数是可选的,所以重构后的findNodes()仍然可以像以前一样使用。

  现在,hide()的实现要简单很多,因为他不需要遍历所有的节点:

var hide = function (node){
    node.style.display = "none";
};

//执行该函数
findNodes(hide);

  如上所示,回调函数可以是一个已有的函数,也可以是一个匿名函数,可以在调用主函数时创建它。例如,下面的代码展示了如何使用同样的findNodes()函数以显示节点:

//传递一个匿名函数
findNodes(function(node) {
    node.style.display = "block";
});

 

回调与作用域

  在前面的例子中,回调执行的语句部分如下:

callback(parameters);

  虽然在大多数情况下,这种方法都是简单而有效的,但经常存在一些场景,其回调并不是一次性的匿名函数或全局函数,而是对象的方法。如果该回调方法使用this来引用他所属的对象,这可能会导致意想不到的行为发生。

// 重构findNodes()以接受一个回调函数
var findNodes = function(callback) {
    var i = 100000,// 大而繁重的循环
        nodes = [],// 存储结果
        found;// 找到了下一个节点

    // 检查回调函数是否为可调用的
    if(typeof callback !== "function") {
        callback = false;
    }

    while(i) {
        i -= 1;
        // 这里是复杂的逻辑...
        // 现在运行回调函数
        if(callback){
            callback(found);
        }
        nodes.push(found);
    }
    return nodes;
}
// 上面是我复制的

var myapp = {};
myapp.color = "green";
myapp.paint = function(node) {
    node.style.color = this.color;
};
findNodes(myapp.paint)

  如果你真的这样做了,那么他肯定不会按照预期那样执行。这是因为this.color没有被定义。因为findNodes()是一个全局函数,所以,this指向了全局对象。类似的,如果findNodes()是一个名为dom的对象的方法(dom.findNodes()),那么回调内部的this将指向dom,而不是预期的myapp。

  对这个问题的解决方案是传递回调函数,并且另外还传递该回调函数所属的对象:

findNodes(myapp.paint,myapp);

  然后,当然还需要修改findNodes()以绑定所传递进入的对象:

 

var findNodes = function(callback,callback_obj) {
    // ...
    if(typeof callback === 'function') {
        callback.call(callback_obj,found);
    }
    // ...
};

  我们发现应用了call(),后面会详细的讲解call()和apply()的相关内容。

  上面的方法,可以稍加优化,无需两次输入该对象的名称:

findNodes('paint',myapp);

  然后,findNodes()内部会有一点小小的变动:

var findNodes = function(callback,callback_obj) {
    // 我们加了一个额外的判断
    if(typeof callback === 'string') {
        callback = callback_obj[callback];
    }
    // ...
    if(typeof callback === 'function') {
        callback.call(callback_obj,found);
    }
    // ...
};

 

异步事件监听

  回调模式有许多常见用途,比如,当附加一个事件监听器到页面上的一个元素时,实际上提供了一个回调函数指针,该函数将会在时间发生时被调用。下面是一个简单的例子,展示了当监听到文档点击事件时如何传递回调函数console.log()。

document.addEventListener("click", console.log, false);

  大多数的客户端浏览器编程都是事件驱动的。比如页面加载完成会触发load事件、用户与页面交互时,将会触发比如click、keypress、mouseover、mousemove等。JavaScript特别适合于事件驱动编程,因为回调模式支持程序以异步方式运行,也就是说,可以乱序方式运行。

 

超时

  使用回调模式的另一个例子是,当使用浏览器的window对象所提供的超时方法:setTimeout()和setInterval()。这些方法也会接受并执行回调函数:

var thePlotThickens = function() {
    console.log("500ms later...");
};

setTimeout(thePlotThickens,500);

  再次强调,这里函数thePlotThickens是如何以变量方式传递的,传递该函数时并没有带括号,因为并不想立即执行该函数,而只是想指向该函数以便setTimeout()在以后使用。

 

库中的回调模式

  回调模式是一种简单而又强大的模式,当设计一个库时他可以派上用场。进入软件库的代码应该尽可能地是通用和可服用的代码。而回调可以帮助实现这种通用化。不需要预测和实现能想到的每一项功能,因为这样会迅速使库膨胀,而绝大多数用户永远不会需要其中大量的功能。相反,可以专注于核心功能并提供“挂钩”形式的回调函数,这将使您很容易的构建、扩展,以及自定义库方法。

 

三、返回函数

  函数也是对象,因此它们也可以被用做返回值。这表示一个函数并不需要以某种数据值或数据数组作为执行结果返回。函数可以返回另一个更专门的函数,也可以按需创建另一个函数,这取决于其输入。

  

var setup = function () {
    alert(1);
    return function() {
        alert(2);
    };
};

// 使用setup函数
var my = setup(); // alerts 1
my(); // alerts 2

// 由于setup()包装了返回函数,它创建了一个闭包,可以使用这个闭包存储一些私有数据,
// 而这些数据仅可被该返回函数访问,但外部代码却无法访问。
// 下面是一个计数器的例子,每次当调用该函数时,它会产生一个递增的值:

var setup1 = function() {
    var count = 0;
    return function () {
        return (count += 1);
    };
};
// 用法
var next = setup1();
console.log(next());
console.log(next());
console.log(next());

  

  这篇主要介绍了回调函数和返回函数。这两种在实际的开发中都十分有价值。一定要认真看哦。下一篇,我们来学习下自定义函数以及即时函数。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/124392.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MacPorts 安装与使用

    MacPorts 安装与使用MacPorts安装与使用安装修改安装源MacPorts使用MacPorts像brew一样,可以快速安装软件。安装在安装macports之前注意要保证当前系统已安装xcode且保持最新版本。https://www.macports.org/install.php官网地址查找对应的系统版本。找到对应版本的dmg安装包,下载后安装。在安装的过程中,会卡在运行软件包步骤。切记断网安装,不进行软件包的安装更新。如果卡在此安装步骤无法退出,可进行如下操作:1.在活动监视器找到对应的安装程序强制退

    2022年9月21日
    2
  • 罗技k580键盘蓝牙连接不上_ipad同时连接蓝牙键盘和耳机

    罗技k580键盘蓝牙连接不上_ipad同时连接蓝牙键盘和耳机K580蓝牙连接,失败解决。

    2022年8月31日
    6
  • linux下vi命令大全

    linux下vi命令大全进入vi的命令 vifilename:打开或新建文件,并将光标置于第一行首 vi+nfilename:打开文件,并将光标置于第n行首 vi+filename

    2022年7月2日
    21
  • 10月22日Ajax培训日记

    10月22日Ajax培训日记今天还是由梁言兵老师讲解Ajax,根据昨天学员的反馈,觉得AjaxTable这个项目太大了,学习起来有点费劲,希望梁老师讲一个较小的综合案例。所以,梁老师先讲解XTree这个客户端JavaScript组件,然后结合XTree,buffalo,spring,hibernate这些框架开发一个Ajax的demo。小收获:varobj={a:’xxx’b:function(){}}定义…

    2025年10月30日
    10
  • ubuntu1804安装教程详细(怎么安装ubuntu18.04)

    UbuntuUbuntu叫法很多(友帮拓、优班图、乌班图),是一个一琢磨应用为主的开源GUN/Linux操作系统,接下来记录了在VMwareWorkstationPro中安装Ubuntu18.04准备工作下载安装VMware虚拟机VMware下载地址:https://www.vmware.com/cn/products/workstation-pro/workstation-pr…

    2022年4月17日
    57
  • glassfish使用jndi 配置mq_glasses的用法

    glassfish使用jndi 配置mq_glasses的用法1.启动服务器。 ①直接运行bin目录下的asadmin.bat就可以②在bin目录运行asadminstart-domaindomain1(打开服务器)asadminstop-domaindomain1 (关闭服务器)

    2022年8月20日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号