`
该用户名已经存在
  • 浏览: 306744 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Javascript 中的回调函数

阅读更多
什么是回调函数?
我的理解,就是采用回调的方式调用函数。那什么又是回调的方式?
举个例子,老师L让学生A做一件事情,就是将全班同学的试卷给学生A,让他找出其中不及格的试卷。老师L接着又找到学生B,将这部分试卷交给他,让学生B把这些同学的名字记下来。看这倒霉老师安排这事儿,一看就不懂回调,首先学生A把全班同学的试卷都翻了个遍,学生
B又将其中不及格部分同学的试卷翻个遍,实际上是多余的for循环了。如果数据量很大(全班300万学生,不及格的140万),那势必会造成多余的时间人力浪费。那怎么用回调的方式呢?学生A说了:老师你给我一个会记名字的学生B,我找到一个不及格的就把试卷给他,他记下试卷上的名字就可以了。这样的话全班同学的试卷就只被找了一遍就完成任务了。那么通过以上的例子,我们就可以把学生B看成是学生A的回调函数。学生B可以看作是老师调用学生A找不及格试卷的方法的时候传给学生A的一个参数,在学生A需要的时候就调用这个参数做事情就可以了。这就可以说是回调的方式。

看看用Javascript表示的回调,首先不用回调的方式:
    //定义函数, 创建html节点集合并返回
    var createNodes = function(sum)
    {
        var nodes = [], //定义节点数组
        node, //节点
        textNode,
        i = sum;
        
        while(i)
        {
            node = document.createElement("div");
            textNode = document.createTextNode
                           ("non-callback-function " + i);
            node.appendChild(textNode);
            nodes.push(node);
            
            i -= 1;
        }
        
        return nodes;
    };  
    
    //定义函数,将树节点集合逐一添加到文档某节点中
    var addNodes = function(child_Nodes, parent_Node)
    {
        var i=0, max = child_Nodes.length;
        
        for(; i<max; i+=1)
        {
            parent_Node.appendChild(child_Nodes[i]);
        }
    }; 
    
    var parent_Node = document.getElementsByTagName("body")[0];
    var child_Nodes = createNodes(10); //循环创建节点集合
    addNodes(child_Nodes, parent_Node); //循环节点集合添加到父节点

很明显的缺点,节点集合被遍历两次,如果遍历节点集合是一个消耗很大的操作,会造成资
源浪费。

当然我们也可以将父节点传给方法createNodes,如:
     var createNodes = function(sum, parent_Node)
     {
         var node, //节点
         textNode,
         i = sum;
        
         while(i)
         {
             node = document.createElement("div");
             textNode = document.createTextNode
               ("non-callback-function " + i);
             node.appendChild(textNode);
             
             parent_Node.appendChild(node); //这个地方将节点加入父节点
            
             i -= 1;
         }
 
     };
     
     var parent_Node = document.getElementsByTagName("body")[0];
     createNodes(100, parent_Node);

这种方式看起来确实更加的简单了。但是我们可以发现,此时的createNodes函数功能变得复杂了,以前该函数的功能只是负责创建指定个数的节点,而现在是负责创建指定个数的节点还要将这些节点添加到指定父节点中。试想如果还需要一个函数的功能是创建指定个数的节点并将这些节点打印出来,那么createNodes函数我们并不能复用,只能重写一个函数了。函数的功能越单一越容易被复用,所以在很多公司都有类似的编码规定,即一个方法不能超过多少行,类不能超过多少行等,归根结底还是为了使方法和类的功能更加单一,更容易被复用。
在Java中,我们可以将createNodes方法的第二个参数传递为一个接口I。在createNodes
方法中将创建的node给接口I的一个方法xxNode(node),至于要将node用来做什么,就看使用者对接口I方法xxNode(node)的具体实现了。这样便很好的实现了代码的复用。但是在
Java中,这样定义好createNodes(sum, I)方法之后,我们并不能将接口O作为第二个参数
传递给createNodes(sum, I)方法,这似乎还不够灵活,当然Java有它的解决办法,比如
让接口O继承接口I等。扯远了,回到Javascript,Javascript是弱类型语言,没有类的概
念,Javascript中的函数就是第一类对象,我们可以将任何的函数作为第二个参数传给
createNodes方法,这似乎比Java更加的灵活。
看例子:
    //添加节点的函数
    var addNode = function(child_Node)
    {
        //该方法只负责向父节点添加一个子节点
        var parent_Node = document.getElementsByTagName("body")[0];
        parent_Node.appendChild(child_Node);
    };
    
    //创建添加子节点函数
    var createNodes = function(sum, callback)
    {
        var node, //节点
        textNode,
        nodes = [],
        i = sum;
        
        if (typeof callback !== "function")
        {
            //弱类型语言的好处,不用重新定义变量接受boolean值。
            callback = false;
        }
        
        while(i)
        {
            node = document.createElement("div");
            textNode = document.createTextNode
                         ("callback-function " + i);
            node.appendChild(textNode);
            nodes.push(node);
            
            //回调
            if (callback)
            {
                //只管将创建的节点给回调方法,至于它拿去做什么不用关心
                callback(node); 
            }
            
            i -= 1;
        }
        
        return nodes;
    };
    
    createNodes(100, addNode);
    //匿名的方式,如果addNode方法确认不用来复用,
     //我们都不用事先声明addNode方法。
    createNodes(100, function(child_Node)
    {
        var parent_Node = document.getElementsByTagName("body")[0];
        parent_Node.appendChild(child_Node);
    });

通过以上代码可以看出Javascript中回调模式的灵活性,可以传递任意方法createNodes
函数,createNodes会将创建好的节点给回调方法,至于你做什么与其无关,乃至可以不用
传回调方法,createNodes依然做好它自己的工作。Javascript中甚至都没有接口的约束。
回调函数中的this问题。
this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的
当前对象。通俗的讲this就是执行该方法的那个对象,如:
    public class User()
    {
        private String name;
        
        public String getName()
        {
            return this.name;
        }
    }
    
    User user = new User();
    user.getName();

那么在执行user.getName();方法的时候,方法getName()中的this就是调用它的当user对象。但是在javascript中,没有类的概念,Javascript中的函数就是第一类对象,那么Javascript函数中的this是谁呢?
其实由于javascript的动态性(解释执行,当然也有简单的预编译过程),this的指向只
有在运行时才确定。我的理解是,Javascript的函数在哪个作用域运行的,this就是该作
用域的提供者。这个特性在给我们带来迷惑的同时也带来了编程上的自由和灵活。
看下面的例子:
    var createNodes = function(sum)
    {
        /**
         * 创建nodes集合的方法如例子1,不再累述。
          */
         return nodes;
    };  
		
    //定义对象
    var addNodeObj = function(child_Nodes)
    {
        node: document.getElementsByTagName("body")[0],
            
        addNode: function(child_Node)
        {
            var i=0, max = child_Nodes.length;
            for(; i<max; i+=1)
            {
                this.node.appendChild(child_Nodes[i]);
            }
        }
     }; 
		
    var child_Nodes = createNodes(10); //循环创建节点集合
    addNodeObj.addNode(child_Nodes); //循环节点集合添加到父节点

由于Javascript是解释执行的,那么在执行addNodeObj.addNode(child_Nodes);
这句话的时候才会去解释执行addNodeObj对象的方法,此时方法addNode属于函数对象addNodeObj的作用域。所以addNode方法中的this就是addNodeObj对象,那this.node当然就是该对像的node属性。
再看一个回调函数的例子:
   //定义对象
    var addNodeObj = function(child_Node)
    {
        node: document.getElementsByTagName("body")[0],
            
        addNode: function(child_Node)
        {
            this.node.appendChild(child_Node);	
        }
    }; 
		
    //创建添加子节点函数
     var createNodes = function(sum, callback)
    {
        //...省略不必要的代码
        if (typeof callback !== "function")
        {
            callback = false;
        }
        
        while(i)
        {
            //.... 
            if (callback)
            {
                callback(node); 
            }
        }
        //...
    };
    
    createNodes(100, addNodeObj.addNode);

可以看到,这段代码中addNodeObj.addNode函数的执行作用域被改变了,即
createNodes函数对象,所以addNode函数中的this就指向createNodes对象,然而该对像中并没有node属性,所以addNode函数执行到 this.node.appendChild(child_Node);的时候会报错。那么在Javascript中怎样来解决这样的问题,Javascript提供了两个非常有用的函数:call和apply函数。
    //定义对象
    var addNodeObj = function(child_Node)
    {
        node: document.getElementsByTagName("body")[0],
            
        addNode: function(child_Node)
        {
            this.node.appendChild(child_Node);	
        }
    }; 
		
    //创建添加子节点函数
     var createNodes = function(sum, callback_obj, callback)
    {
        //...省略不必要的代码
        if (typeof callback !== "function")
        {
            callback = false;
        }
        
        while(i)
        {
            //.... 
            if (callback)
            {
                callback.call(callback_obj, node); 
            }
        }
        //...
    };
    
    createNodes(100, addNodeObj, addNodeObj.addNode);

可以看到在createNodes方法中增加了一个参数callback_obj,callback_obj对象是提供callback函数执行作用域的函数对象。
callback.call(callback_obj, node); 这句话可以理解为callback_obj.callback(node),即改变函数callback的执行作用域为callback_obj即实参addNodeObj,这样实参addNode函数中的this又指向了addNodeObj对象。
关于Javascript函数作用域、call和apply方法的使用和区别、Javascript中的this请参照其他更准确想尽的资料,这篇文主要还是讲Javascript中的回调函数模式。
分享到:
评论

相关推荐

    absl-py-0.1.5.tar.gz

    该资源为absl-py-0.1.5.tar.gz,欢迎下载使用哦!

    Java_SHAFT是一个统一的测试自动化引擎,由一流的框架提供支持。SHAFT提供了一个向导式的语法,可以有效地驱动.zip

    Java_SHAFT是一个统一的测试自动化引擎,由一流的框架提供支持。SHAFT提供了一个向导式的语法,可以有效地驱动

    有色基本金属行业周报美元走强黄金内外价差增长铜铝海外消费偏弱国内需求发力-22页.pdf.zip

    有色基本金属行业周报美元走强黄金内外价差增长铜铝海外消费偏弱国内需求发力-22页.pdf

    基于MATLAB实现的对格子Boltzmann方法中经典的D2Q9模型进行编程求解 单相流体在单通道中的流动情况+使用说明文档

    CSDN IT狂飙上传的代码均可运行,功能ok的情况下才上传的,直接替换数据即可使用,小白也能轻松上手 【资源说明】 基于MATLAB实现的对格子Boltzmann方法中经典的D2Q9模型进行编程求解该程序模拟了单相流体在单通道中的流动情况,在流动通道中可以添加不渗透固体,从而用于模拟流体扰流的情况进一步可以增加不渗透固体的数量,达到模拟流体在多孔介质中的二维流动该程序适用于初学者对LBM方法编程求解的理解,经二次开发后可以用于油气勘探开发领域+使用说明文档 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2020b;若运行有误,根据提示GPT修改;若不会,私信博主(问题描述要详细); 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可后台私信博主; 4.1 期刊或参考文献复现 4.2 Matlab程序定制 4.3 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 5、欢迎下载,沟通交流,互相学习,共同进步!

    Java_处理JBox2D的助手库和示例.zip

    Java_处理JBox2D的助手库和示例

    基于MATLAB实现的单级倒立摆的 T-S 模型包括 LMI 程序源码 +使用说明文档

    CSDN IT狂飙上传的代码均可运行,功能ok的情况下才上传的,直接替换数据即可使用,小白也能轻松上手 【资源说明】 基于MATLAB实现的单级倒立摆的 T-S 模型 包括 LMI 程序源码 ex6.m 模糊控制器S 函数ctrl ex6.m 被控对象S 函数plant+使用说明文档 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2020b;若运行有误,根据提示GPT修改;若不会,私信博主(问题描述要详细); 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可后台私信博主; 4.1 期刊或参考文献复现 4.2 Matlab程序定制 4.3 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 5、欢迎下载,沟通交流,互相学习,共同进步!

    grpcio-1.45.0-cp38-cp38-linux_armv7l.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于Vue的药店后台管理系统设计源码

    药店后台管理系统设计源码:该项目基于Vue开发,包含100个文件,主要使用Vue、JavaScript和HTML语言。该系统是小组项目的一部分,旨在为药店提供一个后台管理系统,包括药品管理、库存管理、订单处理等功能,以提高药店的管理效率和服务质量。

    ntsd,一款超强的进程结束软件

    winxp自带,win7及以上版本把这个文件解压到System32里 ntsd方法1.ntsd -c q -p pid2.ntsd -c q -pn 进程名 资源不贵,快来下载

    大学生毕业答辨ppt免费模板【不要积分】下载可编辑可用(112).zip

    大学生毕业答辨ppt免费模板【不要积分】下载可编辑可用(112).zip

    基于MATLAB实现的语音处理+使用说明文档.zip

    CSDN IT狂飙上传的代码均可运行,功能ok的情况下才上传的,直接替换数据即可使用,小白也能轻松上手 【资源说明】 基于MATLAB实现的语音处理+使用说明文档.zip 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2020b;若运行有误,根据提示GPT修改;若不会,私信博主(问题描述要详细); 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可后台私信博主; 4.1 期刊或参考文献复现 4.2 Matlab程序定制 4.3 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 5、欢迎下载,沟通交流,互相学习,共同进步!

    基于MATLAB实现的用于应用布格重力异常数据反演地下异常密度体+使用说明文档.zip

    CSDN IT狂飙上传的代码均可运行,功能ok的情况下才上传的,直接替换数据即可使用,小白也能轻松上手 【资源说明】 基于MATLAB实现的用于应用布格重力异常数据反演地下异常密度体+使用说明文档.zip 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2020b;若运行有误,根据提示GPT修改;若不会,私信博主(问题描述要详细); 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可后台私信博主; 4.1 期刊或参考文献复现 4.2 Matlab程序定制 4.3 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 5、欢迎下载,沟通交流,互相学习,共同进步!

    基于Python的Struts2全漏洞扫描利用工具设计源码

    Struts2全漏洞扫描利用工具设计源码:该项目基于Python开发,包含21个文件,主要使用Python语言。该工具旨在帮助开发者扫描并利用Struts2中的全漏洞,包括漏洞检测、漏洞利用等功能,以提高网络安全性和系统的安全性。

    STC89C52RC单片机DS18b20温度传感器1602液晶显示软件源码KEIL C51工程文件+开发板PDF原理图.zip

    STC89C52RC单片机DS18b20温度传感器1602液晶显示软件源码KEIL C51工程文件+开发板PDF原理图.zip

    基于MATLAB实现的自动进行八数码问题的最优求解,可以显示步数和过程中的数组状态+使用说明文档.zip

    CSDN IT狂飙上传的代码均可运行,功能ok的情况下才上传的,直接替换数据即可使用,小白也能轻松上手 【资源说明】 基于MATLAB实现的自动进行八数码问题的最优求解,可以显示步数和过程中的数组状态+使用说明文档.zip 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2020b;若运行有误,根据提示GPT修改;若不会,私信博主(问题描述要详细); 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可后台私信博主; 4.1 期刊或参考文献复现 4.2 Matlab程序定制 4.3 科研合作 功率谱估计: 故障诊断分析: 雷达通信:雷达LFM、MIMO、成像、定位、干扰、检测、信号分析、脉冲压缩 滤波估计:SOC估计 目标定位:WSN定位、滤波跟踪、目标定位 生物电信号:肌电信号EMG、脑电信号EEG、心电信号ECG 通信系统:DOA估计、编码译码、变分模态分解、管道泄漏、滤波器、数字信号处理+传输+分析+去噪、数字信号调制、误码率、信号估计、DTMF、信号检测识别融合、LEACH协议、信号检测、水声通信 5、欢迎下载,沟通交流,互相学习,共同进步!

    grpcio-1.42.0-cp36-cp36m-linux_armv7l.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    微信小程序源码 亲子教育新体验 - 仿妈妈课堂App下载

    仿妈妈课堂App是一款专为家长和孩子们设计的亲子教育应用,它提供了丰富的教育资源和互动课程,旨在帮助家长更好地参与到孩子的成长和学习过程中。以下是仿妈妈课堂App的主要特点: 亲子互动课程:提供一系列亲子互动课程,涵盖语言学习、科学探索、艺术创作等多个领域。 专家讲座:定期邀请教育专家进行在线讲座,分享育儿知识和教育理念。 成长记录:允许家长记录孩子的成长点滴,包括学习进度、成就和趣事。 社区交流:构建亲子教育社区,让家长能够分享经验、交流心得。 个性化推荐:根据家长和孩子的兴趣及需求,智能推荐相关教育资源。 安全隐私保护:注重用户隐私和数据安全,提供安全的使用环境。 界面友好:设计简洁明了,易于操作,适合所有年龄段的家长使用。 多平台支持:支持iOS和Android平台,确保家长能够在不同设备上使用。 仿妈妈课堂App是家长和孩子共同成长的理想伴侣,无论是家庭教育还是学校教育的补充,都能提供有力的支持。立即下载仿妈妈课堂App,开启亲子教育的新篇章。

    4G语音质差参数优化调整建议.xlsx

    4G语音质差参数优化调整建议

    Java_芭蕾舞女演员MQTT模块.zip

    Java_芭蕾舞女演员MQTT模块

    基于LSTM的网易云音乐评论分析.zip

    基于LSTM的网易云音乐评论分析LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。

Global site tag (gtag.js) - Google Analytics