This, call, apply, bind

This, call, apply, bind. I refer to articles by Ruan Yifeng, Hu Yu and related articles on “JavaScript Advanced Programming”. Mainly is the summary of relevant knowledge

Directory:

I. Understanding of this

Second, summarize the orientation of this in different occasions and the problems needing attention

Call, bind, apply

4. Write a call

Write apply by hand

Write bind by hand

This in the arrow function

8, This refers to the topic exercise

I. Understanding of this

Reference article: wangdoc.com/javascript/…

This is the object on which the property or method is “currently” located. Since the property or method of an object can be copied to another object, the object on which the property or method is “currently” located is mutable. So the reference to this is also mutable.

The design of this is related to the data structure in memory.

1. When we define a variable:

Var obj = {name: 'zz'};

The JavaScript engine first generates an object {name: ‘zz’} in memory. The address of memory is then assigned to the variable obj. So what’s stored on OBj is actually an address. When we need to read obj.name, we get the address from obj and then fetch the name value. You should have some sense of how variables are described. Storing variables in memory is in the form of. What we read is the value stored on [[vaule]]

{
    name: {
        / / value[[value]] : 'zz',// Is writable
        [[writable]]: true.// Whether enumerable
        [[enumerable]]: true.// Whether it can be configured
        [[configurable]]: true}}Copy the code

2. When we define a function:

The JavaScript engine stores the function separately in memory and then assigns the address of the function to the value of the name property

{

    function f () {
        console.log(this)}; name: {/ / value[[value]]: address of function f,... }}Copy the code

There is a runtime environment when f() is executed alone. Obj is the runtime environment for f when obj.name() is executed. So when other variables in the current environment are referenced in the function body, the values are different depending on the running environment.

    var  f = function () {
        console.log(this.name);
    }

    varName = 'zz';var obj = {
        name: ‘dd’,
        f: f
    }

    f() // This refers to window, this.name zz

    obj.f() // This refers to obj, this.name dd
Copy the code

Hence the sentence: the this in a function (non-arrow function) always refers to the object that last called it.

Second, summarize the direction of this in different occasions

1, global environment: the top-level object window

function f() {
    console.log( this= = =window);
}

f();
Copy the code

Constructor: the instance object to which this points

var  Persion = function (name) {
    this.name = name;
}

var p = newPersion (' zz '); p.name// zz
Copy the code

3. Object methods

If the object’s method contains this, this points to the object on which the method is run. This method assigns to another object, which changes the reference to this.

var obj ={
    foo: function () {
        console.log(this); }}; obj.foo()// obj

/ / a
(obj.foo = obj.foo)() // window

/ / 2
(false || obj.foo)() // window

/ / is three
(1, obj.foo)() // window
Copy the code

Resolution:

Inside the JavaScript engine, obj and obj.foo are stored at two memory addresses, called address one and address two. When obj.foo() is called this way, it calls address two from address one, so address two runs at address one, and this refers to obj.

However, in each of the above cases, we simply fetch address two, assign the value and call it, so the runtime environment is the global environment, so this refers to the global environment.

/ / a

(obj.foo = function () {
    console.log(this); }) ()/ / is equivalent to

(function () {
    console.log(this); }) ()Copy the code
/ / 2
(false || function () {
    console.log(this); }) ()/ / is three

(1.function () {
    console.log(this); }) ()Copy the code

If the method is not in the first layer of the object, this refers only to the current layer of the object and does not inherit from the layer above.


var a = {
    p: 'Hello'.b: {
        m: function() {
          console.log(this.p); }}}; a.b.m()// undefined
Copy the code

4. Problems encountered

(1) Avoid multiple layers of nesting. If nesting is encountered, you need to fix this with a variable on the upper layer, and then call the variable on the inner layer.

var o = {
    f1: function() {
        console.log(this);
        
        var that = this;
        
        var f2 = function() {
          console.log(that); } (); } } o.f1()Copy the code

(2) Avoid this in array handling methods. For example, in foreach, the map callback refers to this as a window. If you want to fix this, you can either define a variable at the top or bind this to the second parameter of foreach

var o = {
    v: 'hello'.p: [ 'a1'.'a2'].f: function f() {
        var that = this;
        this.p.forEach(function (item) {
            console.log(that.v+' '+item);
        });
     }
    
    // Another way
    //this.p.forEach(function (item) {
    // console.log(this.v + ' ' + item);
    //}, this);
}

o.f()

Copy the code

Call, bind, apply

Call () 1, the Function. The prototype.

The call method can specify the point of this inside a function (the scope in which the function is executed).

The call method can take multiple arguments

Func. Call (thisValue, arg1, arg2,...).

The first argument: the object to which this refers. Here thisValue is an object,

(1) If the argument is null, null and undefined are global objects by default

(2) If this is a primitive value, it is converted to the corresponding wrapper object

(3) If it is a normal object, there is no need to do the conversion

var  n = 100;
var obj = {n: 200};

function a () {
    console.log(this.n);
}

function f () {
    return this;
}

a.call() / / 100
a.call(null) / / 100
a.call(undefined) / / 100
a.call(obj) / / 200;
f.call(5) // // Number {[[PrimitiveValue]]: 5}
Copy the code

Other arguments: are the arguments required for a function call

function add (a, b) {
    return a + b;
}

add.call(this.1.2) / / 3

Copy the code

2, the Function. The prototype. The apply ()

The usage is similar to that of call, except that it takes an array as an argument to the function.

Func. Apply (thisValue, (arg1, arg2,...) )

3, the Function. The prototype. The bind ()

(1) Bind () binds this in the function body to an object and returns a new function.

The first argument to bind is some object in the function’s this binding. If the first argument is null, or undefined, this is bound to the global object by default.

var  counter = {
    count: 0.inc: function () {
        this.count++; }}const func = counter.inc.bind(counter);

func() / / 1
Copy the code

(2) Bind can accept many more arguments

var add = function (x, y, z) {
    return x * this.m + y * this.n + z;

}

var obj = {
    m: 2.n: 2
};


var newAdd = add.bind(obj, 5);
const result = newAdd(5.6);
console.log('result----', result);/ / 26


 var newAdd = add.bind(obj, 5.5);
 const result = newAdd(6);
 console.log('result----', result);/ / 26

// Bind binds add with two arguments, and then only passes in the third argument each time newAdd is called.
Copy the code

(3) The bind method returns a new function each time

This way, you cannot cancel the anonymous function

element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));
Copy the code

     

Correct method:


var listener = o.m.bind(o);
element.addEventListener('click', listener);
element.removeEventListener('click', listener);
Copy the code

4. Handwritten Call method

Refer to the article: segmentfault.com/a/119000000…

The call method has the following functions and parameters:

(1) Call changes the direction of this

(2) The call-bound method is executed

(3) Call can carry parameters

Steps of simulation

(1) Set the function to the property of the binding object (null, undefined),

(2) Execute this function and pass related arguments

(3) Delete this function

Function.prototype.call2 = function(obj) {
    const context = obj || window;
    context._func = this;

    let args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }

    constresult = context._func(... args);delete context._func;
    return result;
}
 
var value = 2;

var obj = {
    value: 1
}

function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call2(null); / / 2

console.log(bar.call2(obj, 'kevin'.18));

/* 1, {value: 1, name: kevin; 18} * /

Copy the code

5. The principle of handwritten Apply and call are the same. The only difference is in parameters

Function.prototype.apply2 = function (obj, arr) {
    const context = obj || window;
    context._fn = this;

    var result;
    if(! arr || ! arr.length) { result = context._fn(); }else{ result = context._fn(... arr); }delete context._fn
    return result;

 }

Copy the code

Write bind by hand

Reference article: github.com/mqyqingfeng…

To summarize: The bind() method creates a new function, and when the new function is called, the first argument to bind will be passed as an argument to the function after this when it runs.

Bind returns a function. The first parameter is the operating environment of the new function, which can be passed in as arguments

(1) The binding part of this can be implemented by call and apply.

Function.prototype.bind2 = function (context) {
    var self = this;
    return function () {
        returnself.apply(context); }}Copy the code

(2) Pass parameters

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
}

var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');

Copy the code

From the above example, we know that bindFoo’s arguments contain arguments passed twice


Function.prototype.bind2 = function (context) {
    var self = this;
    // Get the bind2 function from the second argument to the last argument. The argument passed when the bind call is made
    var args = Array.prototype.slice.call(arguments.1);

    return function () {
        // Arguments refer to the arguments passed in by the function returned by bind
        var bindArgs = Array.prototype.slice.call(arguments);
        returnself.apply(context, args.concat(bindArgs)); }}Copy the code

(3) Bind returns a new function, which can be used as a constructor. The value of this specified in bind is invalid, but the value of the parameter passed in is valid.

Function.prototype.bind2 = function (context) {
    // Bind must be a function
    if (typeof this! = ="function") {
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments.1);


    // do the transfer of the prototype chain inheritance
    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
       // If this is the function returned by bind called new, determine what this refers to.
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));

    }

    // Change fBound's.prototype

    fNOP.prototype = this.prototype;

    fBound.prototype = new fNOP();

    return fBound;

}

Copy the code

The above code for call, apply, bind was written by Hu Yui, a good article worth sharing. Make a note here to mark their own understanding

This in the arrow function

Reference article: juejin.cn/post/694602…

When you create the arrow function, you already know its This point. The this inside the arrow function points to the outer this.

So to determine the arrow function this, we must first know the direction of the outer this. Refer to the summary of this reference above

8. Exercises

Reference article:

Juejin. Cn/post / 695904…

Juejin. Cn/post / 706939…

In a real problem, there are all kinds of situations. What to grasp is:

This is bound at run time, not at write time, and its context depends on various conditions at the time the function is called

The binding of this has nothing to do with the position of the function declaration, except how the function is called

About how to call: determine the function call location and call stack, determine the binding object for this.

The first question

    function foo() {
        console.log(this)
        console.log( this.a );
    }

    function doFoo() {
        console.log(this)
        foo();
    }

    var obj = {
      a: 1.doFoo: doFoo
    };

    var a = 2; 
    obj.doFoo()

Copy the code

Correct output:

/* obj:{a: 1.dofoo:} window 2 Obj. DoFoo is called inside of obj. But foo's this binding is the default window foo() output of 2 */
Copy the code

The second question

    var a = 10
    var obj = {
        a: 20.say: () = > {
            console.log(this.a)
        }
    }

    obj.say() 

    var anotherObj = { a: 30 } 
    obj.say.apply(anotherObj) 
Copy the code

The correct output

/* obj.say() is the arrow function inside this. 10 obj.say.apply(anotherObj) now change the operating environment of obj.say to anotherObj, this to the window pointed to by anotherObj: 10 */
Copy the code

The third question

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

a.call(null);
Copy the code

The correct output

 windowThe first argument to call isnullTheta points to thetawindow
Copy the code

The fourth question

var obj = { 
  name : 'cuggz'.fun : function() { 
    console.log(this.name); 
  } 
} 
obj.fun()   
new obj.fun()
Copy the code

The correct output

 /* fun.fun (); /* fun.fun (); Cuggz new obj.fun() takes obj.fun as a constructor. This points to the constructor because there is no name argument, and output: undefined */
Copy the code

The fifth problem

var obj = {
  say: function() {
    // var f1 = () => {
    // console.log("1111", this);
    // }
    // f1();
    const f1 = function() {
        console.log("1111".this);
    }
    f1();
  },
  pro: {
    getPro:() = >  {
        console.log(this); }}}var o = obj.say;
o();
obj.say();
obj.pro.getPro();
Copy the code

Correct output

/* var o = obj.say; I'm assigning the say function to o and o is in global scope; O () output: 1111 window obj.say() where say is run in obj, f1 is the arrow function pointing to the environment in which the outer say is run, so output: 1111 obj If f1 is not an arrow function, f1 is run in window. F1 is called inside the say function, but the default binding is window obj.pro.getPro(). GetPro is the arrow function, and this points to the outer this. So it points to obJ's runtime environment window. Output window * /
Copy the code

The sixth question

    var myObject = {
        foo: "bar".func: function() {
            var self = this;
            console.log(this.foo);  
            console.log(self.foo);  
            (function() {
                console.log(this);
                console.log(this.foo);  
                console.log(self.foo); } ());// var fn = function() {
            // console.log(this);
            // console.log(this.foo);
            // console.log(self.foo);
            // };

            // fn();}}; myObject.func();Copy the code

Is the output

   /* myObject.func() this.foo outputs bar self.foo where this and self refer to the same output bar immediately points to a function where this refers to a window and the function is not bound to func.  Output: undefined self refers to func, and self.foo outputs bar */
Copy the code

Number 7

  window.number = 2;

var obj = {
    number: 3.db1: (function test1() {
       console.log('ddd'.this);
       this.number *= 4;
       return function test() {
         console.log('return'.this);
         this.number *= 5; }}}) ()var db1 = obj.db1;
db1();

obj.db1();
console.log(obj.number);     
console.log(window.number); 
Copy the code

The correct output

/* obj.db1 is an immediate function that is executed immediately when parsed. This. Number = 2 * 4 = 8; var db1 = obj.db1; Db1 points to the return value of the immediate function corresponding to obj. Db1. Db1 () calls the return function of obj. Db1, return obj, This. number = 15. Obj. number is 15. Window. number is 8 */
Copy the code

The eighth problem

var length = 10;

function fn() {
    console.log(this.length);
}
    
var obj = {
    length: 5.method: function(fn) {
        fn();
        arguments[0]();
    }
};
    
obj.method(fn, 1);
Copy the code

Normal output

  /* obj.method(fn, 1); Function fn() {console.log(this.length); function fn() {console.log(this.length); Manufacturer: ƒ, 1, callee: ƒ, Symbol(Symbol. Iterator): ƒ] arguments
Copy the code

Question 9

    var a = 1;
    function printA() {
        console.log(this.a);
    }
    var obj = {
        a: 2.foo: printA,
        bar: function() {
            printA();
        }
    }

    obj.foo(); 
    obj.bar(); 
    var foo = obj.foo;
    foo(); 
   
Copy the code

The correct output

/* obj.foo stores a pointer to printA. Obj.foo () now printA is run in obj this.a 2 printA() inside obj.bar() is run in window 1 foo = obj.foo now assigns a pointer to printA to Foo. The runtime environment is Window. Foo () prints 1 */
Copy the code

The first ten questions

    var x = 3;
    var y = 4;
    var obj = {
        x: 1.y: 6.getX: function() {
            var x = 5;
            return function() {
                return this.x; } (); },getY: function() {
            var y = 7;
            return this.y; }}console.log(obj.getX()) 
    console.log(obj.getY()) 
Copy the code

The correct output

  /* obj.getX() // function() { return this.x; } (); // If the function is running in obj, the output is 6 */
Copy the code

The eleventh problem

var a = 10; 
var obt = { 
    a: 20.fn: function() { 
        var a = 30; 
        console.log(this.a)
    } 
}
obt.fn();  
obt.fn.call();
(obt.fn)();
Copy the code

The correct output

/* obt.fn() runs the environment obt this.a outputs 20 obt.fn.cal() binds the global environment this.a outputs 10 (obt.fn) is the global environment this.a outputs 10 */
Copy the code

Question 12

  function a(xx) {
        this.x = xx;
        return this
    };
    var x = a(5);
    
    var y = a(6);

    console.log(x.x)  
    console.log(y.x)
Copy the code

The correct output

  /* a(5) returns this as window; x = 5; Y = {x: 6} y = {x: 6
Copy the code

Article 13 questions

  function foo(something) {
        this.a = something
    }

    var obj1 = {
        foo: foo
    }

    var obj2 = {}

    obj1.foo(2); 
    console.log(obj1.a); 

    obj1.foo.call(obj2, 3);
    console.log(obj2.a);

    var bar = new obj1.foo(4)
    console.log(obj1.a); 
    console.log(bar.a); 
Copy the code

The correct output

    Call (obj2, 3) obj2.a // 3 obj1.a // 2 bar.a // 4 */
Copy the code

Question 14

    function foo(something){
        this.a = something
    }

    var obj1 = {}

    var bar = foo.bind(obj1);
    bar(2);
    console.log(obj1.a); 

    var baz = new bar(3);
    console.log(obj1.a); 
    console.log(baz.a); 
Copy the code

The correct output

 /* foo. Bind (obj1) replaces this in foo with obj1, which returns bar bar(2) assigning foo 2 obj1.a // 2 new bar(3) constructor, in which case foo. Obj1.a // 2 baz.a // constructor this.a 3 */
Copy the code