“This is the second day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.

preface

When they study the javascript function, several methods are often easily confused, call, apply, bind, caller and callee, the use of these methods, the access to relevant books and materials, compiled a blog, this paper will detail the call, the apply, bind, Caller, callee, etc.

The Function Function

Before we learn about call, apply, bind, Caller, and callee, we need to understand what a function is. After all, these methods are developed around functions.

Function definition:

The Function constructor creates a new Function object. In JavaScript, every Function is actually a Function object, and the Function object generated using the Function constructor is parsed at Function creation time. This is less efficient than if you use function declarations or function expressions and call them in your code, because functions created using the latter are parsed with other code.

  • All arguments passed to the constructor are treated as arguments to the function to be created, with the same identifier name and pass order
  • A function in javascript is an object, and an object is a collection of “key/value” pairs with a hidden connection to the prototype pair
  • Each Function is an instance of the Function type and has attributes and methods like any other reference type
  • Since a function is an object, the function name is actually a pointer to a function object and is not bound to a function

There are two ways to define a function, the first is a function declaration, the second is a function expression

Function declaration

    console.log(foo(10.10));/ / 20
    function foo(a,b){
        return a+b
    }
Copy the code

Functional expression

    console.log(sum(10.10));//TypeError: sum is not a function
    var sum=function(a,b){
        return a+b;
    }
Copy the code

The same way of expressing functions, why is there such a big difference between function declarations and function expressions? And listen to me say slowly, the parser in environment load data, to perform function to function declarations and function expressions are not alike, the parser will be the first to read function declarations, and make it available before perform any code (that is, we often say that the variable), as for the function expression, you must wait until the parser performs to its line of code, Will actually parse the execution.

When evaluating code, the javascript engine declares functions the first time and places them at the top of the source tree, so even if the code declaring the function comes after the code calling it, the javascript engine can promote the function declaration to the top.

No overloading (in depth)

Overloading is a concept used in many programming languages, such as Method overloading in Java, constructor overloading, etc., but JavaScript, a dynamic language, does not have method overloading. Let’s look at an example

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>No overloading (in depth)</title>
    </head>
    <body>
        <script type="text/javascript">
            function addSomeNumber(number){
                return number+100;
            }
            function addSomeNumber(num){
                return num+200;
            }
            var result=addSomeNumber(100);
            console.log(result);/ / 300
        </script>
    </body>
</html>
Copy the code

Obviously, this example declares two functions of the same name, and the result is that the latter function overrides the previous one. The above code is really no different from the following code

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>No overloading (in depth)</title>
    </head>
    <body>
        <script type="text/javascript">
            var addSomeNumber=function(number){
                return number+100;
            }
            addSomeNumber=function(num){
                return num+200;
            }
            var result=addSomeNumber(100);
            console.log(result);/ / 300
        </script>
    </body>
</html>
Copy the code

By rewriting the code, it’s easy to see what’s going on. When you create the second function, you actually override the variable addSomeNumber that references the first function.

As a function of values

Because function names in ECMAScript are themselves variables, functions can also be used as values, meaning that not only can one function be passed to another as a parameter, but a function can also be returned as the result of another function, as is often the case with callback functions.

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>As a function of values</title>
    </head>
    <body>
        <script type="text/javascript">
            function callSomeFunction(someFunction,someArgumet){
                return someFunction(someArgumet);
            }
            function add(num){
                return num+10
            }
            var result1=callSomeFunction(add,10);
            console.log(result1);/ / 20
            function getGreeting(name){
                return 'hello'+name;
            }
            var result2=callSomeFunction(getGreeting,'A stray KK');
            console.log(result2);// Hello a stray Kk
        </script>
    </body>
</html>
Copy the code

The callSomeFunction function takes two arguments. The first argument is a function and the second argument is a value to be passed to the function. Of course, you can return a function from another function, as shown in the following example

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>As a function of values</title>
    </head>
    <body>
        <script type="text/javascript">
            function comPare(propertyName){
                return function(a,b){
                    var value1=a[propertyName];
                    var value2=b[propertyName];
                    if(value1>value2){
                        return -1
                    }else if(value1<value2){
                        return 1;
                    }else{
                        return 0; }}}var data=[{name:'zhangsan'.age:28}, {name:'lisi'.age:29}];
            data.sort(comPare('name'));
            console.log(data);//[{name:'lisi',age:29},{name:'zhangsan',age:28}];
            data.sort(comPare('age'));
            console.log(data);//[{name:'lisi',age:29},{name:'zhangsan',age:28}];
        </script>
    </body>
</html>
Copy the code

The function definition looks a bit complicated, but it’s really just a function nested inside a function with a return operator in front of the inner function. After the inner function receives the propertyName argument, it uses square brackets notation to get the value of the given property. Once it gets the desired property value, Defining the comparison function is straightforward.

Function internal attributes Arguments and this

Inside the function, there are two special objects: Arguments and this, where arguments are an array of classes containing all arguments passed into the function. This refers to the environment object in which the function is executed, or to the value of this (the reference to this is window when the function is called in the global scope of the web page).

arguments

<script type="text/javascript">
            function counter(){
                var sum=0;
                for(var i=0; i<arguments.length; i++){ sum+=arguments[i];
                }
                return sum;
            }
            
            console.log(counter(199.991.1.2.3.4.5));/ / 1205
            console.log(counter());/ / 0
        </script>
Copy the code

Arguments is an implicit object, not declared in a function. An inner function can access any content of an outer function, but it cannot access arguments and this directly

        <script type="text/javascript">
            function f1(){
                console.log(arguments.length);/ / 3
                 f2=function(){
                    console.log(arguments.length);/ / 0
                   }
                return f2;
           }
            var f=f1(1.2.3);
            f();
        </script>
Copy the code

this

        <script type="text/javascript">
            window.color='red';
            var o={
                color:'blue'
            };
            function sayColor(){
                console.log(this.color);//red;
            }
            sayColor();
            o.sayColor=sayColor
            o.sayColor();//blue
        </script>
Copy the code

The function sayColor() is defined in the global scope and refers to this. Since the value of this is not known until the function is called, this may refer to different objects during code execution. When sayColor() is called in the global scope, This refers to the global object window. In other words, evaluating this.color converts to evaluating window.color and returns red. When assigning this function to o and calling O.saycolor (), This refers to the object O, so the this.color evaluation is converted to o.color and returns blue.

Note: The name of a function is simply a variable containing a pointer.

The constructor

Object constructors can create an object in javascript

<script type="text/javascript">
           /* Constructor */
          // Can simply be thought of as a type definition
           function Student(name,age){
                 this.name=name;
                 this.age=age;
                 this.show=function(){
                     console.log(this.name+","+this.age); }}// Call the constructor with the new keyword to create an object Tom
           var rose=new Student("rose".18);
           var jack=new Student("jack".20);
           
           rose.show();//rose,18
           jack.show();//jack,20
        </script>
Copy the code

Call (), apply(), caller, callee, bind()

Properties and methods of a function

callee

When a function is called, its arguments.callee object refers to itself, which is a reference to itself. This may not be very clear, but we’ll go through a case study. The most common example is recursion

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            function factorial(num){
                if(num<=1) {return 1;
                }else{
                    return num*factorial(num-1)}}console.log(factorial(5));/ / 120
        </script>
    </body>
</html>
Copy the code

A very obvious drawback in this example is that the execution of the function is only coupled to the function name factorial, which is programmed with high cohesion and low coupling. To solve this problem, we use arguments.callee instead of the function name.

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            function factorial(num){
                if(num<=1) {return 1;
                }else{
                    return num*arguments.callee(num-1)}}console.log(factorial(5));/ / 120
        </script>
    </body>
</html>
Copy the code

So we have a lot less coupling, and it doesn’t matter what the function name is, how we execute it, okay

caller

This property holds a reference to the function calling the current function, null if the current function is called in global scope.

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>The use of the caller</title>
    </head>
    <body>
        <script type="text/javascript">
            function outer(){
                inner();
            }
            function inner(){
                console.log(inner.caller);
            }
            outer();
        </script>
    </body>
</html>
Copy the code

Results:

Caller = outer(); caller = outer(); caller = outer(); caller = outer(); caller = outer();

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>The use of the caller</title>
    </head>
    <body>
        <script type="text/javascript">
            function outer(){
                inner();
            }
            function inner(){
                console.log(arguments.callee.caller);
            }
            outer();
        </script>
    </body>
</html>
Copy the code

The output is the same as before, here we use arguments.callee instead of inner

Caller D this

This refers to the object that calls the method, and caller refers to the function that calls the function

<script type="text/javascript">
        function add(n)
        {
            console.log("Add is called");
            if(n<=2) {return 1;
            }
            return add.caller(n-1)+add.caller(n-2);
        }
        
        function calc(n){
            console.log("Calc is called");
            return add(n);
        }
        
        / / 1 1 2
        console.log(calc(3));
        </script>
Copy the code

conclusion

  • Each function contains two non-inherited methods, apply() and call(), which are used to call the function in a specific scope and essentially set the value of the this object in the function body.
  • The apply() method takes two arguments, one the scope in which the function is run and the other an array of arguments, and the second argument can be an array example or arguments object.
  • The call() method and the apply() method do the same thing, except that they accept different arguments. In the case of call(), the first argument does not change, but the rest of the arguments are passed directly to the function. In other words, when using call(), the arguments passed to the function must be listed individually.

call()

Function. Call (obj,[param1[,param2[,… [,paramN]]]]) obj: This object will replace this object in Function class params: this object is a parameter list

Calls a method of an object to replace the current object with another

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Use of the call() method</title>
    </head>
    <body>
        <script type="text/javascript">
            function sum(num1,num2){
                return num1+num2;
            }
            function callSum(num1,num2){
                return sum.call(this,num1,num2);
            }
            console.log(callSum(10.10));/ / 20
        </script>
    </body>
</html>
Copy the code

In the case of the call() method, callSum() must be passed each argument explicitly

apply()

The Function. Apply (obj,args) method can take two arguments: obj: this object will replace this object in the Function class. Args: This is an array, which will be passed as arguments to Function (args–>arguments).

Pay attention to

  • If argArray is not a valid array or arguments object, a TypeError will result
  • If neither argArray nor thisObj arguments are provided, the Global object will be used as thisObj and no arguments can be passed
<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Use of the apply() method</title>
    </head>
    <body>
        <script type="text/javascript">
            // Define a human
            function Person(name,age){
                this.name=name;
                this.age=age;
            }
            // Define a student class
            function Student(name,age,grade){
                Person.apply(this.arguments);// This refers to Studnent
                this.grade=grade;
            }
            // Create a student
            var mark=new Student('zhagnsan'.21.'Seventh grade');
            console.log(mark.name,mark.age,mark.grade);/ / zhangsan, 21, grade seven
        </script>
    </body>
</html>
Copy the code

The name and age attributes are not assigned values.

Analysis: the Person. The apply (this, the arguments).

This: Represents student at this point in the creation of the object

Arguments: is an array, i.e. [” zhangsan “, “21”, “first grade”];

Student executes the contents of the Person class, which contains statements such as this.name and so on, and creates attributes in the student object.

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Use of the apply() method</title>
    </head>
    <body>
        <script type="text/javascript">
            function sum(num1,num2){
                return num1+num2;
            }
            function callSum1(num1,num2){
                return sum.apply(this.arguments);
            }
            function callSum2(num1,num2){
                return sum.apply(this,[num1,num2]);
            }
            console.log(callSum1(10.10));/ / 20
            console.log(callSum2(10.10));/ / 20
        </script>
    </body>
</html>
Copy the code

In the above example, callSum1() calls sum() and passes this as the value of this (window object because it is called in global scope) and arguments object. CallSum2 () also calls sum(), Instead, it passes this and an array of arguments, both of which execute normally and return the result.

Note: In strict mode, if a function is called to specify an environment object, the value of this does not transition to window. This is undefined unless you explicitly add a function to an object or call apply() or call()

When do I use apply() and when do I use call()?

In the case of arguments given to an object as an array (as in the Apply example where arguments are passed), the arguments are of array type, and the list of arguments is the same when calling Person. You can use apply, If my parameter list for Person looks like this (age,name) and my parameter list for Student looks like this (name,age,grade), then I can use call to specify the position of the parameter list (Person.call(this,age,name, grade));

In fact, passing parameters isn’t really where Apply () and call() come in. What’s really powerful about them is their ability to extend the scope in which the function runs, as shown in the following example

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            window.color='red';
            var o={
                color:'blue'
            }
            function sayColor(color){
                console.log(this.color)
            }
            sayColor();//red
            sayColor.call(this);//red
            sayColor.call(window);//red
            sayColor.call(o);//blue
        </script>
    </body>
</html>
Copy the code

The biggest benefit of using call() or apply() to extend the scope is that there is no coupling between the object and the method

bind()

This method creates an instance of a function whose this value is bound to the value passed to bind()

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Use of the bind method</title>
    </head>
    <body>
        <script type="text/javascript">
            window.color='red';
            var o={
                color:'blue'
            }
            function sayColor(color){
                console.log(this.color)
            }
            var objSayColor=sayColor.bind(o);
            objSayColor();//blue
        </script>
    </body>
</html>
Copy the code

Here sayColor() creates the objSayColor() function by calling bind and passing in the object O. The value of this in the objSayColor() function is equal to O, so blue will be seen even if the function is called in the global scope

length

The number of named arguments specified when declaring a function

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Function</title>
    </head>
    <body>
        <h2>Function - length</h2>
        <script>
            function f1(n1,n2)
            {
                console.log("Number of arguments actually entered:"+arguments.length);/ / 1
            }
            console.log("Number of named parameters defined:"+f1.length);
            f1(1);
            f1(1.2.3);
        </script>
    </body>
</html>
Copy the code

The beauty of the apply() method

There are many advantages of apply in practice, and I would like to add two points here. One is the weakness of array push, and the other is the use of math.max (), math.min () for maximum, minimum, and so on.

(1)Array.prototype.push can merge two arrays

We know that the array push method does not provide push of an array, but provides push(param1,param… ParamN), so you can also apply this array. Let’s look at an example

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Application 1 of Apply</title>
    </head>
    <body>
        <script type="text/javascript">
            var arr1=[1.2.3];
            var arr2=[4.5.6];
            Array.prototype.push.apply(arr1,arr2);
            console.log(arr1);/ / [6]
        </script>
    </body>
</html>
Copy the code

You can also understand that ARR1 calls the push method, and the arguments are a collection of arguments assembled by apply

(2)Math.max() and math.min () find the maximum and minimum value of the array

Math.max(a,b) and math.min (a,b) can only evaluate the maximum and minimum values of two numbers, but when we want to evaluate the maximum and minimum values of an array, we can not do this using the apply method, as shown in the following example

<! DOCTYPEhtml>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Application 2 of apply</title>
    </head>
    <body>
        <script type="text/javascript">
            var arr=[1.2.3.4.5];
            console.log(Math.max.apply(null,arr));/ / 5
            console.log(Math.min.apply(null,arr));/ / 1
        </script>
    </body>
</html>
Copy the code

We’ve seen how clever use of apply makes it very easy to maximize and minimize math.max () and math.min ()

conclusion

Call, bind, and apply all take this as their first argument, but the second argument is different

  • Function. Call (this.obj,arg0,arg1…);
  • Apply (thisObj,[arguments])
  • Bind takes the same arguments as Call, except that it returns a function

Of course, the three parameters are not limited to string type, but can be various types, including functions, objects, and so on