Explain the magic of apply, call and bind in Javascript

Reprinted from: http://www.admin10000.com/document/6711.html, thanks to the author, it is very clear.

apply and call

  In javascript, both call and apply exist to change the context of a function's runtime, in other words, to change the point of this inside the function body.

  A major feature of JavaScript is that functions have concepts such as "definition-time context" and "run-time context" and "context can be changed".

  First come a chestnut:
function fruits() {}
  
fruits.prototype = {
    color: "red",
    say: function() {
        console.log("My color is " + this.color);
    }
}
  
var apple = new fruits;
apple.say();    //My color is red

  But if we have an object banana= {color : "yellow"} , and we don't want to redefine the say method on it, then we can use apple's say method by calling or applying:
banana = {
    color: "yellow"
}
apple.say.call(banana);     //My color is yellow
apple.say.apply(banana);    //My color is yellow

  Therefore, it can be seen that call and apply appear to dynamically change this. When an object does not have a method (banana does not have a say method in this chestnut), but others do (apple has a say method in this chestnut), we can Use the methods of other objects to operate with call or apply.

  The difference between apply and call

  For both apply and call, the functions are exactly the same, but the way of accepting parameters is different. For example, there is a function defined as follows:

var func = function (arg1, arg2) {
     
};

  It can be called as follows:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])

  Where this is the context you want to specify, it can be any JavaScript object (everything in JavaScript is an object), call needs to pass parameters in order, and apply puts parameters in an array.

  In JavaScript, the number of parameters of a function is not fixed, so to say the applicable conditions, use call when your parameters are clearly known.

  And when you are not sure, use apply, and then push the parameters into the array and pass them in. When the number of parameters is uncertain, the function can also traverse all parameters through the arguments array.

  In order to consolidate and deepen memory, some common usages are listed below:

  1. Append between arrays
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为  [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

  2. Get the maximum and minimum values ​​in an array
var numbers = [5, 458, 120, -215];
var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

  number itself does not have a max method, but Math does, and we can use its method with call or apply.

  3. Verify whether it is an array (provided that the toString() method has not been rewritten)
function isArray(obj){
    returnObject.prototype.toString.call(obj) === '[object Array]' ;
}

  4. Class (pseudo) arrays use array methods
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

  There is an object structure in Javascript called a pseudo-array. More special is the arguments object, as well as calling getElementsByTagName, document.childNodes and the like, they return NodeList objects are pseudo-arrays. The push, pop and other methods under Array cannot be applied.

  But we can convert it to a real array object with length property through Array.prototype.slice.call, so that domNodes can apply all the methods under Array.

  In -depth understanding of using apply and call

  Let's borrow an interview question to understand apply and call more deeply.

  Define a log method so that it can proxy the console.log method. The common solution is:
function log(msg) {
  console.log(msg);
}
log(1);    //1
log(1,2);    //1

  The above method can solve the most basic needs, but when the number of incoming parameters is uncertain, the above method will fail. At this time, you can consider using apply or call. Note that how many parameters are passed in is uncertain. , so it is best to use apply as follows:
function log(){
  console.log.apply(console, arguments);
};
log(1);    //1
log(1,2);    //1 2

  The next requirement is to add a "(app)" prefix to each log message, for example:
log("hello world");    //(app)hello world

  How to do it more elegantly? At this time, you need to think that the arguments parameter is a pseudo array, which is converted into a standard array by Array.prototype.slice.call, and then use the array method unshift, like this:
function log(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('(app)');
  
  console.log.apply(console, args);
};


 bind

  finished apply and call, let's talk about bind. The bind() method is very similar to apply and call, and it can also change the pointer of this in the function body.

  The explanation of MDN is: The bind() method will create a new function, called the binding function. When this binding function is called, the binding function will use the first parameter passed to the bind() method when it is created as this , the second and subsequent parameters passed to the bind() method plus the parameters of the binding function runtime itself are used as the parameters of the original function in order to call the original function.

  Let's see how to use it directly. In the common singleton pattern, we usually use _this , that , self , etc. to save this , so that we can continue to refer to it after changing the context. like this:
var foo =
    bar : 1,
    eventBind: function(){
        var _this = this;
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(_this.bar);     //1
        });
    }
}

  Due to the Javascript-specific mechanism, the context changes when eventBind:function(){ } transitions to $('.someClass').on('click',function(event) { }), and the above methods use variables to save this All are useful and no problem. Of course, using bind() can solve this problem more elegantly:
var foo =
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            /* Act on the event */
            console.log(this.bar);      //1
        }.bind(this));
    }
}

  In the above code, bind() creates a function, and when the click event is bound to be called, its this keyword will be set to the value passed in (here refers to the value passed in when calling bind() parameter). So, here we pass the desired context this (actually foo ) into the bind() function. Then, when the callback function is executed, this will point to the foo object. Another simple chestnut:
var bar = function () {
console.log(this.x);
}
var foo =
x:3
}
bar(); // undefined
var func = bar.bind (foo);
func(); // 3

  Here we create a new function func. When a bound function is created using bind(), when it is executed, its this will be set to foo instead of the global scope like when we call bar() .

  There is an interesting question, if you bind() twice in a row, or bind() three times in a row, what is the output value? like this:
var bar = function () {
    console.log(this.x);
}
var foo =
    x:3
}
var sed = {
    x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
  
var fiv = {
    x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?

  The answer is that both times it will still output 3 instead of the expected 4 and 5. The reason is that in Javascript, multiple bind() is invalid. For a deeper reason, the implementation of bind() is equivalent to using a function to wrap a call / apply internally. The second bind() is equivalent to wrapping the first bind() again, so the bind after the second time is ineffective.

 Comparison of apply, call, and bind

  So what are the similarities and differences between apply, call, and bind? When to use apply, call and when to use bind. A simple chestnut:
var obj = {
    x: 81,
};
  
var foo =
    getX: function() {
        return this.x;
    }
}
  
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81

  The three outputs are all 81, but pay attention to the use of the bind() method, which has more parentheses after it.

  That is, the difference is that you use the bind() method when you want the context to be changed, not immediately, but a callback. On the other hand, apply/call executes the function immediately.

  To summarize again:

apply , call , and bind are all used to change the point of the this object of the function;
the first parameter of apply , call , and bind is the object to be pointed to by this, that is, the context you want to specify;
Apply , call , bind can use subsequent parameters to pass parameters;
bind returns the corresponding function, which is convenient for later calling; apply and call are called immediately.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327102785&siteId=291194637