• 函数调用_通过apply和call方法调用


    不同类型函数调用之间的主要区别在于:最终作为函数上下文(可以通过this参数隐式引用到)传递给执行函数对象不同。对于方法而言,即为所在的对象;对于函数而言是window或是undefined(取决于是否处于严格模式下);
    对于构造函数而言是一个新创建的对象实例。

    <!DOCTYPE html>
    <html lang="en">
    <head>  
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>为函数绑定特定的上下文</title>
        <script src="../unitl/test.js"></script>
        <style>
            #results li.pass {color:green;}
            #results li.fail {color:red;}
        </style>
    </head>
    <body>
        <button id="test">Click Me</button>
        <ul id="results"></ul>
    </body>
    <script>
    
        
        function Button() {
            this.clicked = false;
            //单击事件处理器的声明函数。由于该函数是对象的方法,所以在函数中使用this来获取对象的引用。
            this.click = function name(params) {
                this.click =  true;
                //在该方法中,我们测试了按钮是否在单击后正确改变了状态
                assert(button.clicked,"The button hs been clicked");
            }
            
        }
    
        //创建一个用于跟踪是否被单击的实例
        var button = new Button();
    
        //在按钮上添加单击处理器
        var elem = document.getElementById("test");
        elem.addEventListener("click",button.click);
    
    </script>
    </html>   
    

    在这个例子中,我们定义了一个按钮,并且想知道他是否曾被单击过。为了保存单击状态的状态信息,我们使用构造函数创建一个名为button的实例化对象,通过该对象我们可以存储被
    单击的状态:

          function Button() {
            this.clicked = false;
            //单击事件处理器的声明函数。由于该函数是对象的方法,所以在函数中使用this来获取对象的引用。
            this.click = function name(params) {
                this.click =  true;
                //在该方法中,我们测试了按钮是否在单击后正确改变了状态
                assert(button.clicked,"The button hs been clicked");
            }
            
        }
        var button = new Button();
    

    在该对象中,我们还得意了click方法作为单击按钮式触发的事件处理函数,该方法将clicked属性设置为true,然后测试实例化对象的状态是否正确(我们有意使用button标识符而非this关键字)----它们应该具有相同的指向,
    (但事实果真如此吗?)。最后,我们创建了button.click方法作为按钮的单击处理函数。

          var elem = document.getElelemtById('test');
          elem.addEventListener("click",button.click);
    

    当我们在浏览器中加载示例代码并单击按钮时,显示结果如下图所示,红色部分的文件表示测试失败了,在本例中的代码之所以测试失败,是由于click函数的上下文对象并非像我们预期的一样指向button对象。

    我们的测试为何失败了?我们对按钮单击状态的改变去哪儿了?通常情况下,事件回调函数的上下文是粗发事件的对象(在本例中是HTML的按钮,而非button对象)。回想本章前面部分的内容,如果通过button.click()调用函数,上下文将是按钮,因为函数将作为button对象的方法被调用。但在这个例子中,浏览器的事件处理系统将把调用的上下文定义为事件触发的目标
    元素,因此上下文将是<button>元素,而非button对象。所以我们将单击状态设置到了错误的对象上。
    这是一个令人惊讶的常见问题,本章的后面将会介绍如何 避免出现这种情况。 现在探讨一个如何解决它:可以使用apply和call方法显式得设置函数上下文。

    使用apply和call方法

    javascript为我们提供了一种调用函数的方式,从而可以显式地指定任何对象作为函数的上下文。我们可以使用每个函数上都存在的这两种方法来完成:apply和call。
    我们所指的正是函数的方法。作为第一类对象(顺便说一下,函数是由内置的Function构造函数所构建),我们可以像其他对象类型一样拥有属性,也包括方法。
    若想使用apply方法调用函数,需要为其传递两个参数:作为函数上下文的对象和一个数组作为函数调用的参数。call方法的使用方式类似,不同点在于是直接以参数列表的形式,而不再是作为数组传递。
    下面例子演示了这两种方法的实际使用方式

    <!DOCTYPE html>
    <html lang="en">
    <head>  
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>使用apply和call方法来设置函数上下文</title>
        <script src="../unitl/test.js"></script>
        <style>
            #results li.pass {color:green;}
            #results li.fail {color:red;}
        </style>
    </head>
    <body>
        <ul id="results"></ul>
    </body>
    <script>
    
        //函数"处理"了参数,并将结果result变量放在一个作为该函数上下文的对象上。
        function juggle() {
            var result = 0;
            for(var n=0; n<arguments.length; n++) {
    
                result += arguments[n];
    
            }
            this.result = result;
        }
        //这些对象的初始值为空,它们会作为测试对象
        var ninja1 = {};
        var ninja2 = {};
    
    
        //使用apply方法向ninja1传递一个参数数组
        juggle.apply(ninja1,[1,2,3,4]);
        //使用call方法向ninja2传递一个参数列表
        juggle.call(ninja2,5,6,7,8);
    
    
        //测试展现了传入juggle方法中的对象拥有了结果值。
        assert(ninja1.result ===10,"juggled via apply");
        assert(ninja2.result === 26, "juggled via call");
    
    
    </script>
    </html>
              
    

    在这个例子中,我们定义了一个名为juggle的函数,函数的作用是将所有的参数加载一起并存储在函数上下文的result属性中(通过this关键字引用)。看起来似乎是一个不太实用的函数,但它能够帮助我们验证函数的传参是否正确,以及哪个对象最终作为函数上下文。
    然后设置两个对象:ninja1和ninja2,我们将使用这两个对象作为函数上下文,第一个对象连同一个参数数组一起传递给函数的apply方法,将第二个对象连同参数列表传递给函数的call方法。

          juggle.apply(ninja1,[1,2,3,4]);
          juggle.call(ninja2,5,6,7,8);
    

    值得注意的是,apply和call之间唯一的不同之处在于如何传递参数。在使用apply的情况下,我们使用参数数组;在使用call的情况下,我们则在函数上下文之后依次列出调用参数。

    上图传入的call和apply方法的第一个参数都会被作为函数上下文,不同处在于后续的参数。apply方法只需要额外的参数,也就是一个包含参数的数组;call方法则需要传入任意数量的参数值
    这些参数将用作函数的实参。
    现在已经提供了行数上下文和参数,接下来继续测试!首先,检查传递给apply方法的ninja1对象,他应该拥有一个result属性,并存储了所有参数(1,2,3,4) 的和,同样,传递给call方法的ninja2对象的result属性值应该等于参数5、6、7、8的和。

        assert(ninja1.result ===10,"juggled via apply");
        assert(ninja2.result === 26, "juggled via call");
    

    call和apply这两个方法对于我们要特殊指定一个函数的上下文对象时特别有用,在执行回调函数时可能会经常用到。

    function juggle() {
            var result = 0;
            for(var n=0; n<arguments.length; n++) {
    
                result += arguments[n];
    
            }
            this.result = result;
    }
    var ninja1 = {}
    var ninja2 = {}
    


    使用call和apply方法手动设置函数上下文,产生函数上下文(this)与arguments。

  • 相关阅读:
    javascript类的封装『转』
    闭包概念
    继承
    理解面向对象
    cookie的使用
    开园啦。。。
    Myslq 之常用命令
    Myslq 之修改提示符
    Myslq 之登陆、退出
    Javascript 之 Number
  • 原文地址:https://www.cnblogs.com/jamal/p/14137742.html
Copyright © 2020-2023  润新知