首页 文章

Javascript call()&apply()vs bind()?

提问于
浏览
670

我已经知道 applycall 是类似的函数,它们设置 this (函数的上下文) .

不同之处在于我们发送参数的方式(手动与数组)

Question:

但什么时候应该使用 bind() 方法?

var obj = {
  x: 81,
  getX: function() {
    return this.x;
  }
};

alert(obj.getX.bind(obj)());
alert(obj.getX.call(obj));
alert(obj.getX.apply(obj));

jsbin

16 回答

  • 40

    以简单形式回答

    • Call调用该函数并允许您逐个传入参数 .

    • Apply调用该函数并允许您将参数作为数组传递 .

    • Bind返回一个新函数,允许你传入一个这个数组和任意数量的参数 .


    应用与呼叫与绑定示例

    Call

    var person1 = {firstName: 'Jon', lastName: 'Kuperman'};
    var person2 = {firstName: 'Kelly', lastName: 'King'};
    
    function say(greeting) {
        console.log(greeting + ' ' + this.firstName + ' ' + this.lastName);
    }
    
    say.call(person1, 'Hello'); // Hello Jon Kuperman
    say.call(person2, 'Hello'); // Hello Kelly King
    

    Apply

    var person1 = {firstName: 'Jon', lastName: 'Kuperman'};
    var person2 = {firstName: 'Kelly', lastName: 'King'};
    
    function say(greeting) {
        console.log(greeting + ' ' + this.firstName + ' ' + this.lastName);
    }
    
    say.apply(person1, ['Hello']); // Hello Jon Kuperman
    say.apply(person2, ['Hello']); // Hello Kelly King
    

    Bind

    var person1 = {firstName: 'Jon', lastName: 'Kuperman'};
    var person2 = {firstName: 'Kelly', lastName: 'King'};
    
    function say() {
        console.log('Hello ' + this.firstName + ' ' + this.lastName);
    }
    
    var sayHelloJon = say.bind(person1);
    var sayHelloKelly = say.bind(person2);
    
    sayHelloJon(); // Hello Jon Kuperman
    sayHelloKelly(); // Hello Kelly King
    

    何时使用每个

    通话和申请是可以互换的 . 只需确定发送数组或逗号分隔的参数列表是否更容易 .

    我总是记得哪一个是通过记住Call是逗号(分隔列表)而Apply是for Array .

    绑定有点不同 . 它返回一个新函数 . Call和Apply立即执行当前功能 .

    Bind非常适合很多事情 . 我们可以使用它来调整上面例子中的函数 . 我们可以使用一个简单的hello函数并将其转换为helloJon或helloKelly . 我们也可以将它用于像onClick这样的事件,我们不知道什么时候它们会被解雇,但我们知道我们希望它们有什么背景 .

    Referance:codeplanet.io

  • 50

    当我们想要为具有特定上下文的函数分配例如时,应该使用绑定函数 .

    var demo = {
               getValue : function(){ 
                 console.log('demo object get value       function') 
                }
               setValue : function(){  
                  setTimeout(this.getValue.bind(this),1000)           
               }
     }
    

    在上面的例子中,如果我们调用demo.setValue()函数并直接传递this.getValue函数,那么它不会直接调用demo.setValue函数,因为这在setTimeout中引用了window对象,所以我们需要将demo对象上下文传递给this.getValue使用bind的函数 . 它意味着我们只传递函数与demo对象的上下文而不是实际调用函数 .

    希望你明白 .

    欲了解更多信息,请参阅javascript bind function know in detail

  • 28

    它们都将 this 附加到函数(或对象)中,区别在于函数调用(见下文) .

    callthis 附加到函数中并立即执行该函数:

    var person = {  
      name: "James Smith",
      hello: function(thing) {
        console.log(this.name + " says hello " + thing);
      }
    }
    
    person.hello("world");  // output: "James Smith says hello world"
    person.hello.call({ name: "Jim Smith" }, "world"); // output: "Jim Smith says hello world"
    

    bindthis 附加到函数中,它需要像这样单独调用:

    var person = {  
      name: "James Smith",
      hello: function(thing) {
        console.log(this.name + " says hello " + thing);
      }
    }
    
    person.hello("world");  // output: "James Smith says hello world"
    var helloFunc = person.hello.bind({ name: "Jim Smith" });
    helloFunc("world");  // output: Jim Smith says hello world"
    

    或者像这样:

    ...    
    var helloFunc = person.hello.bind({ name: "Jim Smith" }, "world");
    helloFunc();  // output: Jim Smith says hello world"
    

    apply 类似于 call ,除了它采用类似数组的对象而不是一次列出一个参数:

    function personContainer() {
      var person = {  
         name: "James Smith",
         hello: function() {
           console.log(this.name + " says hello " + arguments[1]);
         }
      }
      person.hello.apply(person, arguments);
    }
    personContainer("world", "mars"); // output: "James Smith says hello mars", note: arguments[0] = "world" , arguments[1] = "mars"
    
  • 16

    Call apply and bind. and how they are different.

    让我们学习电话并使用任何日常术语进行申请 .

    你有三辆汽车 your_scooter , your_car and your_jet ,它们以相同的机制(方法)开始 . 我们使用方法 push_button_engineStart 创建了一个对象 automobile .

    var your_scooter, your_car, your_jet;
    var automobile = {
            push_button_engineStart: function (runtime){
            console.log(this.name + "'s" + ' engine_started, buckle up for the ride for ' + runtime + " minutes");
        }
    }
    

    让我们了解什么时候打电话和申请使用 . 让我们假设你是一名工程师,你有 your_scooteryour_caryour_jet ,它没有附带push_button_engine_start,你希望使用第三方 push_button_engineStart .

    如果运行以下代码行,则会出错 . 为什么?

    //your_scooter.push_button_engineStart();
    //your_car.push_button_engineStart();
    //your_jet.push_button_engineStart();
    
    
    automobile.push_button_engineStart.apply(your_scooter,[20]);
    automobile.push_button_engineStart.call(your_jet,10);
    automobile.push_button_engineStart.call(your_car,40);
    

    因此,上面的示例成功地为your_scooter,your_car,your_jet提供了来自汽车对象的功能 .

    Let's dive deeper 这里我们将拆分上面的代码行 . automobile.push_button_engineStart 帮助我们获得所使用的方法 .

    此外,我们使用点符号应用或调用 . automobile.push_button_engineStart.apply()

    现在应用并调用accept两个参数 .

    • 上下文

    • 参数

    所以这里我们在最后一行代码中设置上下文 .

    automobile.push_button_engineStart.apply(your_scooter,[20])

    Difference between call and apply 只是应用接受数组形式的参数,而调用只是可以接受逗号分隔的参数列表 .

    what is JS Bind function?

    绑定函数基本上绑定某事物的上下文,然后将其存储到变量中以便在稍后阶段执行 .

    让我们前面的例子更好 . 之前我们使用了属于汽车对象的方法并用它来装备 your_car, your_jet and your_scooter . 现在让我们想象一下,我们想单独给出一个单独的 push_button_engineStart ,以便在我们希望执行的任何后期阶段单独启动我们的汽车 .

    var scooty_engineStart = automobile.push_button_engineStart.bind(your_scooter);
    var car_engineStart = automobile.push_button_engineStart.bind(your_car);
    var jet_engineStart = automobile.push_button_engineStart.bind(your_jet);
    
    
    setTimeout(scooty_engineStart,5000,30);
    setTimeout(car_engineStart,10000,40);
    setTimeout(jet_engineStart,15000,5);
    

    still not satisfied?

    让我们把它说成泪滴 . 是时候试验了 . 我们将返回调用并应用函数应用程序并尝试存储函数的值作为参考 .

    下面的实验失败,因为调用和apply立即被调用,因此,我们永远不会进入将变量存储在变量中的阶段,这是绑定函数窃取节目的地方

    var test_function = automobile.push_button_engineStart.apply(your_scooter);

  • 388

    想象一下,绑定不可用 . 你可以很容易地构建它如下:

    var someFunction=...
    var objToBind=....
    
    var bindHelper =  function (someFunction, objToBind) {
        return function() {
            someFunction.apply( objToBind, arguments );
        };  
    }
    
    bindHelper(arguments);
    
  • 2

    call/apply 立即执行功能:

    func.call(context, arguments);
    func.apply(context, [argument1,argument2,..]);
    

    bind 不立即执行函数,但返回包装的apply函数(以便以后执行):

    function bind(func, context) {
        return function() {
            return func.apply(context, arguments);
        };
    }
    
  • 4
    • Call调用该函数并允许您逐个传入参数 .

    • Apply调用该函数并允许您将参数作为数组传递 .

    • Bind返回一个新函数,允许你传入一个这个数组和任意数量的数组参数 .

  • 93
    function sayHello() {
                //alert(this.message);
                return this.message;
        }
        var obj = {
                message: "Hello"
        };
    
        function x(country) {
                var z = sayHello.bind(obj);
                setTimeout(y = function(w) {
    //'this' reference not lost
                        return z() + ' ' + country + ' ' + w;
                }, 1000);
                return y;
        }
        var t = x('India')('World');
        document.getElementById("demo").innerHTML = t;
    
  • -1

    这里有一个good article来说明 bind()apply()call() 之间的区别,总结如下 .

    • bind() 允许我们在调用函数或方法时轻松设置将绑定到哪个特定对象 .
    // This data variable is a global variable​
    var data = [
        {name:"Samantha", age:12},
        {name:"Alexis", age:14}
    ]
    var user = {
        // local data variable​
        data    :[
            {name:"T. Woods", age:37},
            {name:"P. Mickelson", age:43}
        ],
        showData:function (event) {
            var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1​
            console.log (this.data[randomNum].name + " " + this.data[randomNum].age);
        }
    }
    
    // Assign the showData method of the user object to a variable​
    var showDataVar = user.showData;
    showDataVar (); // Samantha 12 (from the global data array, not from the local data array)​
    /*
    This happens because showDataVar () is executed as a global function and use of this inside 
    showDataVar () is bound to the global scope, which is the window object in browsers.
    */
    
    // Bind the showData method to the user object​
    var showDataVar = user.showData.bind (user);
    // Now the we get the value from the user object because the this keyword is bound to the user object​
    showDataVar (); // P. Mickelson 43​
    
    • bind() 允许我们借用方法
    // Here we have a cars object that does not have a method to print its data to the console​
    var cars = {
        data:[
           {name:"Honda Accord", age:14},
           {name:"Tesla Model S", age:2}
       ]
    }
    
    // We can borrow the showData () method from the user object we defined in the last example.​
    // Here we bind the user.showData method to the cars object we just created.​
    cars.showData = user.showData.bind (cars);
    cars.showData (); // Honda Accord 14​
    

    这个例子的一个问题是我们在 cars 对象上添加了一个新方法 showData ,我们可能不想这样做只是为了借用一个方法,因为cars对象可能已经有一个属性或方法名称 showData . 我们不想意外覆盖它 . 我们将在下面对 ApplyCall 的讨论中看到,最好使用 ApplyCall 方法借用一个方法 .

    • bind() 允许我们讨论一个函数

    Function Currying,也称为部分函数应用程序,是使用一个函数(接受一个或多个参数),该函数返回一个已经设置了一些参数的新函数 .

    function greet (gender, age, name) {
        // if a male, use Mr., else use Ms.​
        var salutation = gender === "male" ? "Mr. " : "Ms. ";
        if (age > 25) {
            return "Hello, " + salutation + name + ".";
        }else {
            return "Hey, " + name + ".";
        }
     }
    

    我们可以使用 bind() 来讨论这个 greet 函数

    // So we are passing null because we are not using the "this" keyword in our greet function.
    var greetAnAdultMale = greet.bind (null, "male", 45);
    
    greetAnAdultMale ("John Hartlove"); // "Hello, Mr. John Hartlove."
    
    var greetAYoungster = greet.bind (null, "", 16);
    greetAYoungster ("Alex"); // "Hey, Alex."​
    greetAYoungster ("Emma Waterloo"); // "Hey, Emma Waterloo."
    
    • apply()call() 设置此值

    applycallbind 方法都用于在调用方法时设置此值,并且它们以稍微不同的方式执行,以允许在JavaScript代码中使用直接控制和多功能性 .

    设置此值时, applycall 方法几乎完全相同,只是将函数参数作为数组传递给 apply () ,而必须单独列出参数以将它们传递给 call () 方法 .

    下面是一个使用 callapply 在回调函数中设置它的示例 .

    // Define an object with some properties and a method​
    // We will later pass the method as a callback function to another function​
    var clientData = {
        id: 094545,
        fullName: "Not Set",
        // setUserName is a method on the clientData object​
        setUserName: function (firstName, lastName)  {
            // this refers to the fullName property in this object​
            this.fullName = firstName + " " + lastName;
        }
    };
    
    function getUserInput (firstName, lastName, callback, callbackObj) {
         // The use of the Apply method below will set the "this" value to callbackObj​
         callback.apply (callbackObj, [firstName, lastName]);
    }
    
    // The clientData object will be used by the Apply method to set the "this" value​
    getUserInput ("Barack", "Obama", clientData.setUserName, clientData);
    // the fullName property on the clientData was correctly set​
    console.log (clientData.fullName); // Barack Obama
    
    • 使用 applycall 借用函数

    • 借用数组方法

    让我们创建一个 array-like 对象并借用一些数组方法来操作类似数组的对象 .

    // An array-like object: note the non-negative integers used as keys​
    var anArrayLikeObj = {0:"Martin", 1:78, 2:67, 3:["Letta", "Marieta", "Pauline"], length:4 };
    
     // Make a quick copy and save the results in a real array:
     // First parameter sets the "this" value​
     var newArray = Array.prototype.slice.call (anArrayLikeObj, 0);
     console.log (newArray); // ["Martin", 78, 67, Array[3]]​
    
     // Search for "Martin" in the array-like object​
     console.log (Array.prototype.indexOf.call (anArrayLikeObj, "Martin") === -1 ? false : true); // true​
    

    另一种常见情况是将 arguments 转换为数组如下

    // We do not define the function with any parameters, yet we can get all the arguments passed to it​
     function doSomething () {
        var args = Array.prototype.slice.call (arguments);
        console.log (args);
     }
    
     doSomething ("Water", "Salt", "Glue"); // ["Water", "Salt", "Glue"]
    
    • 借用其他方法
    var gameController = {
         scores  :[20, 34, 55, 46, 77],
         avgScore:null,
         players :[
              {name:"Tommy", playerID:987, age:23},
              {name:"Pau", playerID:87, age:33}
         ]
     }
     var appController = {
         scores  :[900, 845, 809, 950],
         avgScore:null,
         avg     :function () {
                 var sumOfScores = this.scores.reduce (function (prev, cur, index, array) {
                      return prev + cur;
             });
             this.avgScore = sumOfScores / this.scores.length;
         }
       }
       // Note that we are using the apply () method, so the 2nd argument has to be an array​
       appController.avg.apply (gameController);
       console.log (gameController.avgScore); // 46.4​
       // appController.avgScore is still null; it was not updated, only gameController.avgScore was updated​
       console.log (appController.avgScore); // null​
    
    • 使用 apply() 执行变量功能

    Math.max是变量函数的一个例子,

    // We can pass any number of arguments to the Math.max () method​
    console.log (Math.max (23, 11, 34, 56)); // 56
    

    但是,如果我们有一组数字传递给 Math.max 怎么办?我们不能这样做:

    var allNumbers = [23, 11, 34, 56];
    // We cannot pass an array of numbers to the the Math.max method like this​
    console.log (Math.max (allNumbers)); // NaN
    

    这是 apply () 方法帮助我们执行可变参数函数的地方 . 而不是上述,我们必须使用 apply ( )传递数字数组,因此:

    var allNumbers = [23, 11, 34, 56];
    // Using the apply () method, we can pass the array of numbers:
    console.log (Math.max.apply (null, allNumbers)); // 56
    
  • 1

    它允许设置 this 的值,与函数的调用方式无关 . 这在使用回调时非常有用:

    function sayHello(){
        alert(this.message);
      }
    
      var obj = {
         message : "hello"
      };
      setTimeout(sayHello.bind(obj), 1000);
    

    使用 call 获得相同的结果将如下所示:

    function sayHello(){
        alert(this.message);
      }
    
      var obj = {
         message : "hello"
      };
      setTimeout(function(){sayHello.call(obj)}, 1000);
    
  • 7

    假设我们有 multiplication 功能

    function multiplication(a,b){
    console.log(a*b);
    }
    

    让我们使用 bind 创建一些标准函数

    var multiby2 = multiplication.bind(this,2);

    现在multiby2(b)等于乘法(2,b);

    multiby2(3); //6
    multiby2(4); //8
    

    如果我在bind中传递两个参数怎么办?

    var getSixAlways = multiplication.bind(this,3,2);
    

    现在getSixAlways()等于乘法(3,2);

    getSixAlways();//6
    

    甚至传递参数返回6; getSixAlways(12); //6

    var magicMultiplication = multiplication.bind(this);
    

    这将创建一个新的乘法函数并将其分配给magicMultiplication .

    哦不,我们将乘法功能隐藏在magicMultiplication中 .

    调用 magicMultiplication 返回空白 function b()

    在执行它工作正常 magicMultiplication(6,5); //30

    打电话和申请怎么样?

    magicMultiplication.call(this,3,2); //6

    magicMultiplication.apply(this,[5,2]); //10

    简单来说, bind 创建函数, callapply 执行函数,而 apply 期望数组中的参数

  • 0
    function printBye(message1, message2){
    console.log(message1 + " " + this.name + " "+ message2);
    }
    
    var par01 = { name:"John" };
    var msgArray = ["Bye", "Never come again..."];
    
    printBye.call(par01, "Bye", "Never come again...");//Bye John Never come again...
    printBye.call(par01, msgArray);//Bye,Never come again... John undefined
    //so call() doesn't work with array and better with comma seperated parameters 
    
    //printBye.apply(par01, "Bye", "Never come again...");//Error
    printBye.apply(par01, msgArray);//Bye John Never come again...
    
    var func1 = printBye.bind(par01, "Bye", "Never come again...");
    func1();//Bye John Never come again...
    
    var func2 = printBye.bind(par01, msgArray);
    func2();//Bye,Never come again... John undefined
    //so bind() doesn't work with array and better with comma seperated parameters
    
  • -3

    如果希望稍后使用特定上下文调用该函数,请使用 .bind() ,这在事件中很有用 . 如果要立即调用该函数,请使用 .call().apply() ,然后修改上下文 .

    调用/应用立即调用该函数,而 bind 返回一个函数,该函数在稍后执行时将具有用于调用原始函数的正确上下文集 . 这样,您可以在异步回调和事件中维护上下文 .

    我做了很多:

    function MyObject(element) {
        this.elm = element;
    
        element.addEventListener('click', this.onClick.bind(this), false);
    };
    
    MyObject.prototype.onClick = function(e) {
         var t=this;  //do something with [t]...
        //without bind the context of this function wouldn't be a MyObject
        //instance as you would normally expect.
    };
    

    我在Node.js中广泛使用它来进行异步回调,我想传递一个成员方法,但仍希望上下文成为启动异步操作的实例 .

    一个简单,天真的bind实现就像:

    Function.prototype.bind = function(ctx) {
        var fn = this;
        return function() {
            fn.apply(ctx, arguments);
        };
    };
    

    它还有更多内容(比如传递其他args),但是你可以阅读更多关于它的信息并查看真正的实现on the MDN .

    希望这可以帮助 .

  • 700

    Function.prototype.call()Function.prototype.apply()都调用具有给定 this 值的函数,并返回该函数的返回值 .

    另一方面,Function.prototype.bind()创建一个具有给定 this 值的新函数,并返回该函数而不执行它 .

    那么,让我们看一个如下所示的函数:

    var logProp = function(prop) {
        console.log(this[prop]);
    };
    

    现在,让我们看一个看起来像这样的对象:

    var Obj = {
        x : 5,
        y : 10
    };
    

    我们可以将函数绑定到我们的对象,如下所示:

    Obj.log = logProp.bind(Obj);
    

    现在,我们可以在代码中的任何位置运行 Obj.log

    Obj.log('x'); // Output : 5
    Obj.log('y'); // Output : 10
    

    它真正变得有趣的地方是,当你不仅为 this 绑定一个值,而且为它的参数绑定 prop

    Obj.logX = logProp.bind(Obj, 'x');
    Obj.logY = logProp.bind(Obj, 'y');
    

    我们现在可以这样做:

    Obj.logX(); // Output : 5
    Obj.logY(); // Output : 10
    
  • 17

    bind :它使用提供的值和上下文绑定函数,但它不执行该函数 . 要执行功能,您需要调用该函数 .

    call :它使用提供的上下文和参数执行函数 .

    apply :它使用提供的上下文和 parameter as array 执行函数 .

  • 5

    我认为它们的相同之处是:它们都可以改变函数的这个值 . 它们的区别在于:bind函数将返回一个新函数作为结果; call和apply方法会立即执行该函数,但apply可以接受一个数组作为params,它会解析分离的数组 . 而且,bind函数可以是Currying .

相关问题