Magical use of apply, call, bind in Javascript

This article is really difficult to write, because there are so many related articles on the Internet.

Coincidentally, I saw a sentence in an article by Mr. Ruan a few days ago:

"For me, blogging is a knowledge management tool first and a dissemination tool second. My technical articles are mainly used to organize knowledge that I don't know yet. I only write about things that I don't fully grasp yet, those What I'm proficient at is often not motivated to write. Showing off is never my motivation, curiosity is."

For this sentence, I can't agree more, and it also made me determined to write this article. Although there are many articles on the Internet, most of them are copied and pasted, and they are obscure and difficult to understand. I hope that through this article, I can clearly improve my understanding of apply and call. , bind understanding, and list some of their magical uses to deepen memory.

   apply、call 

In javascript, both call and apply exist to change the context in which a function runs, 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 apply and call, the function is exactly the same, but the way of accepting parameters is different. For example, there is a function defined as follows:

1

2

3

var func = function(arg1, arg2) {

     

};

It can be called as follows:

1

2

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 is an object in JavaScript), 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 pseudo array of arguments.

 

To consolidate and deepen memory, here are some common usages:

Append between arrays

1

2

3

4

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] */

Get the maximum and minimum value in an array

1

2

3

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.

Verify whether it is an array (provided that the toString() method has not been overridden)

1

2

3

functionisArray(obj){ 

    return Object.prototype.toString.call(obj) === '[object Array]' ;

}

Class (pseudo)arrays use array methods

1

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 calls like getElementsByTagName , document.childNodes and the like, which 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 the use of apply, call

Let's borrow an interview question to gain a deeper understanding of apply and call.

Define a log method so that it can proxy the console.log method. The common solution is:

1

2

3

4

5

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);
};


   Detailed explanation of bind

After talking about 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


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: 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, not like when we call bar() global scope.

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.

   apply, call, bind comparison

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


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. The three outputs are all 81, but pay attention to the use of the bind() method, which has more parentheses after it.

 

To summarize again:

  • apply , call , bind are all used to change the point of the this object of the function;
  • The first parameter of apply , call , bind is the object to which this points to, that is, the context you want to specify;
  • apply , call , bind can use subsequent parameters to pass parameters;
  • bind is to return the corresponding function, which is convenient to be called later; apply and call are to be called immediately.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324969546&siteId=291194637