`
该用户名已经存在
  • 浏览: 308611 次
  • 性别: 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中的回调函数模式。
分享到:
评论

相关推荐

    JavaScript中回调函数的使用方法.pdf

    在回调函数中处理错误通常意味着在回调函数中实现错误检查,并在必要时抛出错误或执行其他错误处理逻辑。 ```javascript function fetchData(url, callback) { // 假设这是一个异步的数据获取函数 // ... const ...

    理解和使用 JavaScript 中的回调函数

    ### 理解和使用 JavaScript 中的回调函数 在 JavaScript 中,回调函数是一种常见的功能编程技术,被广泛应用于各种场景之中。本文旨在深入探讨回调函数的概念、工作原理以及如何在实际开发中应用它们。 #### 一、...

    js回调函数示例

    JavaScript回调函数是异步编程的一种常见模式,它在JavaScript中扮演着至关重要的角色,尤其是在处理I/O操作、网络请求和时间间隔等耗时任务时。本文将深入探讨JavaScript回调函数的概念、工作原理以及如何在实际...

    回调函数demo

    - 定时器:在JavaScript中,`setTimeout`和`setInterval`函数接受一个回调函数,指定在一定时间后执行的代码。 五、回调地狱与解决方案 虽然回调函数为异步编程提供了便利,但当多个异步操作串联起来时,可能导致...

    JavaScript回调函数面试题.zip

    - **错误处理**:在回调函数中,错误处理通常通过在回调函数的第一个参数接收错误对象(如`err`)来实现。如果没有错误,则`err`为`null`。 **5. 高阶函数与回调** 高阶函数是可以接受一个或多个函数作为参数,...

    JavaScript中的回调函数实例讲解

    这样我们就可以在回调函数中处理数据,无论数据是成功加载还是出现了错误。 理解回调函数的工作原理及其在JavaScript中的应用对于掌握JavaScript这门语言至关重要。对于熟悉Java等其他编程语言的开发者而言,回调...

    javascript回调函数详解参考.docx

    以下是一些关于JavaScript回调函数的关键点: 1. **回调函数定义**:回调函数是一个作为参数传递给其他函数的函数,这个参数通常是一个函数引用。当外部函数执行完毕或满足特定条件时,它会调用这个传递进来的函数...

    ocx中事件函数,调用js中的回调函数

    3. **OCX事件触发**:当OCX控件的某个事件发生时,其内部的事件函数会被执行,然后这个函数会查找并调用预先注册的JavaScript回调函数。 4. **数据传递**:事件函数可以通过参数或者某种约定的方式(如通过全局变量...

    帮助理解回调函数的简洁实例

    如果在执行过程中出现错误,错误对象会被填充并传递给回调,这样可以在回调函数中进行错误处理。 4. **回调地狱** 当多个异步操作连续使用回调函数时,代码会形成“回调地狱”,即嵌套的回调函数结构,这使得代码...

    LCalendar最新版 移动端时间控件添加回调函数

    改动主要集中在JavaScript文件上,添加了名为"DaDaCallBack"的回调函数。这个回调函数的目的是在用户在LCalendar中选择完日期或时间并点击确定按钮后执行。通过这样的设计,开发者可以自定义在用户完成日期选择后的...

    JavaScript回调函数面试题.docx

    ### JavaScript回调函数知识点详解 #### 一、回调函数的基本概念 **回调函数**是JavaScript中一种重要的编程模式,尤其在处理异步操作时显得尤为重要。简单来说,回调函数就是一个通过参数传递给另一个函数的函数...

    经典回调函数实例

    回调函数在编程中是一种常见的设计模式,特别是在异步编程中,它被广泛应用于JavaScript、Python等语言中。回调函数的基本思想是将一个函数作为参数传递给另一个函数,然后在内部函数执行完毕后调用这个传入的函数。...

    浅析JavaScript回调函数应用_.docx

    JavaScript回调函数是编程中一种常见的技术,特别是在异步编程领域。回调函数允许我们将一个函数作为参数传递给另一个函数,以便在特定条件满足时执行。这种技术在JavaScript中尤为重要,因为JavaScript是单线程的,...

    回调函数回调函数回调函数

    回调函数在计算机编程中是一种非常重要的机制,它允许我们将一个函数作为参数传递给另一个函数,以便在特定条件或事件发生时由被调用的函数执行。这种设计模式在很多编程语言中都有应用,如C、C++、JavaScript、...

    javascript 回调函数示例

    javascript 回调函数示例

    测试 回调函数

    在JavaScript等语言中,回调函数经常用于处理异步操作,例如文件读取、网络请求等。当这些操作完成后,我们不希望阻塞主线程的执行,而是希望在操作完成时得到通知。这就是回调函数发挥作用的地方。例如,我们可以...

    js回调函数的使用技巧和认识

    js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数js回调函数...

    callback手把手教你写回调函数源代码

    在JavaScript、C++、C#等语言中,回调函数都是实现异步处理和事件处理的关键。 在Linux环境中,回调函数常用于系统调用、信号处理和定时器等场景。例如,当一个进程接收到信号时,可以注册一个回调函数来处理这个...

    我整理的回调函数callbackfunction

    回调函数在编程中扮演着重要角色,特别是在异步编程领域。回调函数是一种处理程序,它作为其他函数的参数被传递,以便在特定事件发生或任务完成时被调用。这种设计模式广泛应用于JavaScript、Python、C++等多语言中...

    CallBack回调函数

    5. **回调函数与闭包**:在JavaScript中,回调函数常常与闭包结合使用,以便在异步操作完成后访问和操作外部作用域的变量。 6. **回调函数与模块化**:回调函数是模块化设计的一个关键组成部分,它使得不同功能的...

Global site tag (gtag.js) - Google Analytics