Learn more about apply, call, and bind

This is the 7th day of my participation in Gwen Challenge

When writing programs, we all know that this works well, but it’s easy to misuse. As I started learning arrow function as I know the arrow to refer to this, but I don’t know where it refers to, so he wrote in the application, will take it for granted write, cause sometimes crazy’ve picked by a data acquisition is less than, this intangible costs will increase a lot of time, don’t understand the principle of nonsense always easy afterwards two lines of tears (T_T)

In the following article, we will explain the application scenarios of this and understand what apply, bind and call are.

First, talk about the understanding of this object

  • This, the context in which the function is executed, always refers to the direct (not indirect) caller of the function, and can be changed by apply, call, or bind.

  • If you have the new keyword, this refers to the object that new came out of.

  • In an event, this points to the object that triggered the event. In particular, this in An attachEvent in IE always points to the global object Window.

  • For anonymous functions or directly called functions, this refers to the global context (window for browser, global for NodeJS), and the rest of the function calls, this refers to whoever called it.

  • For es6 arrow functions, the point of the arrow function depends on where the arrow function is declared, and where this is declared, this points.

The application scenario of this

In the program, this mainly has the following five application scenarios:

  • Called as a normal function
  • usecallapplybindIs called
  • Called as an object method
  • inclassMethod is called
  • The arrow function is called

1. Called as a normal function

When this is called as a normal function, it points to the Window global.

function fn1(){
    console.log(this);
}

fn1(); //window
Copy the code

2. Be called using call, apply, and bind

When this is called using call, apply, and bind, it points directly to the contents of the scope.

function fn1(){
    console.log(this);
}

fn1(); //window

fn1.call({ x : 100 }); //{x : 100}
fn1.apply({x : 200}); //{x : 200}

const fn2 = fn1.bind({ x : 200 });
fn2(); //{ x : 200 }
Copy the code

Called as an object method

As you can see from the following code, when this is inside the sayHi() method, it is called as a zhangsan method that points to the current object. In wait(), there is a timer and a function inside the timer, so the second this is called as a normal function, pointing to the window global.

const zhangsan = {
    name: 'Joe'.sayHi(){
        // This is the current object
        console.log(this);
    },
    wait(){
        setTimeout(function(){
            //this === window
            console.log(this); }); }}Copy the code

Called in the class method

As you can see from the following code, when this is called in class, it refers to the entire object.

class People{
    constructor(name){
        this.name = name;
        this.age = 20;
    }
    sayHi(){
        console.log(this); }}const zhangsan = new People('Joe');
zhangsan.sayHi(); / / zhangsan object
Copy the code

5. The arrow function is called

See the following code, careful partners are not difficult to find, and we see the above point 3 seems to be somewhat similar, the main difference is that the timer function is changed to arrow function. When this is changed to an arrow function, this points to a zhangsan object, not the whole object.

const zhangsan = {
    name: 'Joe'.sayHi(){
        // This is the current object
        console.log(this);
    },
    waitAgain(){
        setTimeout(() = > {
            // This is the current object
            console.log(this); }); }}Copy the code

Having finished with arrow functions, let’s sort out the differences between arrow functions and normal functions, and whether arrow functions can be considered constructors.

(1) Definition of arrow function and ordinary function

Normal functions are defined by the function keyword. This cannot be used with lexical scope and is bound at run time, depending only on how the function is called, where it is called, and where it is called. (Depends on the caller, and whether it runs independently)

Arrow functions are defined using what is called the “fat arrow” operation =>. Arrow functions do not apply the four rules of the normal function this binding, but determine this based on the outer (function or global) scope, and the binding of the arrow function cannot be modified (nor can new).

(2) Difference between arrow function and ordinary function

  • Arrow functions are often used in callback functions, including event handlers or timers.
  • The sum of arrow functionsvar self = this, both attempt to replace the traditional this runtime mechanism by pulling the this binding back into lexical scope.
  • Arrow functions have no prototype, nonethis, nosuper, there is noarguments, there is nonew.target.
  • Arrow function cannot passnewKeyword call.
    • There are two methods inside a function:[[Call]][[Construct]], through thenewIs executed when a function call is made[[construct]]Method to create an instance object, and then execute the function body, will functionthisBind to this instance object.
    • Executes when called directly[[Call]]Method to execute the function body directly.
    • Arrow function doesn’t have any[[Construct]]Method that cannot be used as a constructor call when usednewAn error occurs when making a function call.
function foo(){
    return (a) = > {
        console.log(this.a); }}let obj1 = {
    a: 2
};

let obj2 = {
    a: 3
};

let bar1 = foo.call(obj1);
let bar2 = bar1.call(obj2);

console.log(bar1); // 2 [Function (anonmous)]
// console.log(bar2); // 2 undefind
Copy the code

(3) Four rules for this binding

The four rules for the this binding follow the following order:

New Binding > Show Binding > Implicit Binding > Default binding

Here are the four rules.

  • The default binding: No other modifiers (bindapplycall), define pointing to global objects in non-strict mode, and define pointing to global objects in strict modeundefined
function foo() { 
    console.log(this.a); 
}
var a = 2; 
foo(); //undefined
Copy the code
  • Implicit binding: Whether the call location existsContext object, or whether it is owned or contained by an object, then the implicit binding rule will call the function inthisBind to this context object. Also, the object property chain is onlyOne level up or one level downPlays a role in the call location.
function foo() { 
    console.log(this.a); 
}
var obj = { 
    a: 2.foo: foo, 
}
obj.foo(); / / 2
Copy the code
  • Explicitly bound: by running on a functioncallapply, to explicitly bindthis
function foo() { 
    console.log(this.a); 
}
var obj = { 
    a: 2 
};
foo.call(obj); / / 2
Copy the code

Shows the hard binding of the binding

function foo(something) { 
    console.log(this.a, something); 
    return this.a + something; 
}
function bind(fn, obj) { 
    return function() {
        return fn.apply(obj, arguments); 
    }; 
}
var obj = { 
    a: 2 
}
var bar = bind(foo, obj);
console.log(bar); //f()
Copy the code
  • The New binding:newCalling the function creates a brand new object and binds this object to the function callthis.NewBinding, if yesnewA hard-bound function, then will usenewThe new object replaces the hard bindingthis
function foo(a) { 
    this.a = a; 
}
var bar = new foo(2); 
console.log(bar.a); / / 2
Copy the code

Apply, call, and bind

1. Common use of apply, call and bind

The common use of all three is that you can change the this direction of a function and bind the function to its context. Here is an application scenario to deepen understanding:

let obj1 = {
    hobby: 'running'.add(favorite){
        console.log('In my spare time, I like${favorite}But I like it at the same timeThe ${this.hobby}`); }}let obj2 = {
    hobby: 'learning'
}

obj1.add('reading'); // In my spare time, I like reading, but I also like running
Copy the code

You can see that in the last line of code, we call the add function in obj1 and pass in a parameter reading. The “this” in the add function refers to its object, obj1, so this.hobby is running, but what if we want to get the hobby of obj2? This refers to the apply, call, and bind we often hear.

Let’s start with apply, call, and bind.

2, apply

Array.prototype. Apply (this, [args1, args2]).

(2) Pass in parameters:

The first argument is passed to the object to which this points, that is, to whomever this points in the function.

The second argument: Passes in an array containing the arguments needed by the function.

(3) the functions of apply: ① Call function; ② Specify the reference to this in the function.

(4) Code demonstration:

/ * * * *@description Implement the apply function, encapsulating myApply on the function prototype to achieve the same effect as the native Apply function */

Function.prototype.myApply = function(context){

    // Store the target object to be moved
    _this = context ? Object(context) : window;

    // Set a unique attribute on the object that passes this and assign the function to it
    let key = Symbol('key');
    _this[key] = this;

    // Call the function as an argument to separate the arguments stored in the array
    let res = arguments[1] ? _this[key](...arguments[1]) : _this[key]();

    / / delete
    delete _this[key];

    // Return the value returned by the function
    return res;
}

Copy the code

(5) Previously on

Once myApply is implemented, we continue with the original hobby example to modify the reference to this.

let obj1 = {
    hobby: 'running'.add(. favorite){ / /... Favorite means you can accept multiple parameters
        console.log('In my spare time, I like${favorite}But I like it at the same timeThe ${this.hobby}`); }}let obj2 = {
    hobby: 'learning'
}

obj1.add.myApply(obj2, ['reading'.'working']); 

// Output: In my spare time, I like reading and working, but I also like learning
Copy the code

In obj1.add.myapply (obj2, [‘reading’, ‘working’]), the first argument points this of the add function in obj1 to obj2, and the second argument is passed as an array of arguments, As an argument passed to the add function in obj1, it can finally print both reading and working.

3, call

Array.prototype.call(this, args1, args2)

(2) Pass in parameters:

The first argument is passed to the object to which this points, that is, to whomever this points in the function.

Other parameters: in addition to the first parameter, the other parameters need to be passed in several, just one by one.

(3) Call function; ② Specify the reference to this in the function.

(4) Code demonstration:

/ * * * *@description Implement the apply function, encapsulating myApply on the function prototype to achieve the same effect as the native Apply function */

Function.prototype.myCall = function(context){

    // Store the target object to be moved
    let _this = context ? Object(context) : window;

    // Set a unique attribute on the object that passes this and assign the function to it
    let  key = Symbol('key');
    _this[key] = this;

    // Create an empty array to store multiple incoming parameters
    let args = [];

    // Add all the parameters passed to the new array
    for(let i =1; i < arguments.length; i++){
        args.push(arguments[i]);
    }

    // Pass the new array as multiple arguments and call the function
    letres = _this[key](... args);/ / delete
    delete _this[key];

    // Return the value returned by the function
    return res;
}

Copy the code

(5) Previously on

Once we’ve implemented myCall, we’ll go back to our original hobby example and modify the reference to this.

let obj1 = {
    hobby: 'running'.add(. favorite){ / /... Favorite means you can accept multiple parameters
        console.log('In my spare time, I like${favorite}But I like it at the same timeThe ${this.hobby}`); }}let obj2 = {
    hobby: 'learning'
}

obj1.add.myCall(obj2, 'reading'.'working');

// Output: In my spare time, I like reading and working, but I also like learning
Copy the code

In obj1.add.myCall(obj2, ‘reading’, ‘working’), the first argument points obj1’s add function’s this to obj2, and the second argument points to obj2 by passing in multiple arguments, As an argument passed to the add function in obj1, it can finally print both reading and working.

Here, let’s sort out the differences between call and apply:

The only difference between Call and apply is the way arguments are passed to a function. Call passes multiple arguments one by one, while Apply passes multiple arguments together in an array.

4, the bind

Array.prototype.bind(this, args1, args2).

(2) Pass in parameters:

The first argument is passed to the object to which this points, that is, to whomever this points in the function.

Other parameters: Except for the first parameter, all parameters can be passed as array types like apply or one by one like call. Note that the rest of the parameters need to be passed in parentheses.

(1) Clone the current function, return the clone function; ② The new cloned function whose this is specified.

(4) Code demonstration:

/ * * *@description Bind (myBind) : myBind (myBind) : myBind (myBind) : myBind (myBind

Function.prototype.myBind = function(context){

    // Store the target object to be moved
    let _this = context ? Object(context) : window;

    // Set a unique attribute on the object that passes this and assign the function to it
    let key = Symbol('key');
    _this[key] = this;

    // Create a function closure
    return function(){

        // Add all parameters to the new array in order to support the need for multiple parameters and array parameters
        letargs = [].concat(... arguments);// Call the function
        letres = _this[key](... args);/ / delete
        delete _this[key];

        // Return the value returned by the function
        returnres; }}Copy the code

(5) Previously on

Once we’ve implemented myBind, we’ll go back to the original hobby example and modify the reference to this.

let obj1 = {
    hobby: 'running'.add(. favorite){ / /... Favorite means you can accept multiple parameters
        console.log('In my spare time, I like${favorite}But I like it at the same timeThe ${this.hobby}`); }}let obj2 = {
    hobby: 'learning'
}

obj1.add.myBind(obj2)(['reading'.'working']);

// Output: In my spare time, I like reading and working, but I also like learning
Copy the code

As you can see, bind is a bit like apply and call combined, except that it returns a function that needs to be called again. The arguments passed to bind can take the form of arrays like apply. It can also be passed in one by one like call.

You don’t want to bother putting a little parenthesis behind this, but that’s the power of bind, and sometimes bind is used a lot in currizing functions.

At this point, the relevant knowledge about this is finished! So let’s conclude.

5. Make a summary

  • The value of this is determined at function execution time, not at function definition time.

  • Apply, call, and bind are all methods of functions that can change the this reference of a function.

  • Both Apply and call change the reference of the function this and call the function immediately after passing in the argument.

  • Bind returns a new function after changing this and passing in an argument.

  • The arguments passed in by apply are arrays, the arguments passed in by call are sequentially separated by commas, and the arguments passed in by bind are either arrays or sequentially.

Fourth, concluding remarks

About this point to the question is particularly common in the front end of the interview, we can in accordance with the order of the above knowledge points series together to understand! At the same time, the content of this paper is sorted for my own understanding, which may lead to boundary ambiguity and other problems. If you do not understand or wrong place welcome comment area comments or private letter I exchange ~

  • Concern about the public number on Monday laboratory, do not regularly share learning dry goods

  • If this article is useful to you, be sure to like it and follow it