[R1-2]this,call,apply

1. this

Except for the less commonly used with and eval, the pointers of this are roughly divided into the following four types:

  • Use as a method of an object
  • Called as a normal function
  • Constructor use
  • Function.prototype.call and Function.prototype.apply calls

1.1 Method calls as objects

When a function is called as a method of an object, this refers to the object. If the method of the object is aliased, this points to the global object, as shown in the following code.

const person = {
    userName: 'Mango',
    sayName () {
        console.log(this);
        console.log(this.userName);
    }
}
person.sayName();       // Mango
// alias
let sayName = person.sayName;    
sayName();              // undefined at this point this points to the global object

1.2 Ordinary function call

Ordinary functions are divided into: ES5 functions and arrow functions (ES6).

  • For functions in ES5, this always points to the global object;
  • ES6 arrow function, this is related to the context when it is defined
<body>
<div id="myDiv"> I am a DIV </div>

<script>
    const divDom = document.querySelector('#myDiv');

    divDom.addEventListener('click', function () {
        // this points to divDom
        console.log('anonymous function this: ', this);
        let callback = function () {
            // this points to window
            console.log('in anonymous function callback this: ', this);
        }
        let callback2 = () => {
            // this points to divDom
            console.log('in anonymous function callback(arrow function) this: ', this);
        }
        callback();
        callback2();
    })
    divDom.addEventListener('click', () => { // At this point the context is in the global environment
        // this points to window
        console.log('arrow function this: ', this);
        let callback = function () {
            // this points to window
            console.log('in arrow function callback this: ', this);
        }
        callback();
    })

    const obj = {
        addEvent () {
            divDom.addEventListener('click', () => { // At this point the context is in the obj object environment
                // this points to the obj object
                console.log('inside the object of the arrow function this: ', this);
                let callback = function () {
                    // this points to window
                    console.log('In an anonymous function inside an object callback this: ', this);
                }
                callback();
            })
        }
    }
    obj.addEvent();
</script>
</body>

1.3 Constructor calls

When a function is called with the new operator, the function always returns an object, and usually, this in the constructor points to the returned object.

const Person = function () {
    this.name = 'Mango';
}

const person = new Person();
console.log('person name: ', person.name);

1.4 call and apply

Function.prototype.call and Function.prototype.apply can dynamically change the this point, the only difference between the two is the calling method.

const obj1 = {
    name: 'Mango',
    getName () {
        return this.name;
    }
}
const obj2 = {
    name: 'Mango2'
}
console.log(obj1.getName());    // Mango
console.log(obj1.getName.call(obj2));       // Mango2

1.5 The disappearing this

In Section 1.1, when an object's method is assigned to a variable (similar to aliasing), this no longer refers to the object, but to the global object. as follows:

const person = {
    userName: 'Mango',
    sayName () {
        console.log(this);
        console.log(this.userName);
    }
}
let sayName = person.sayName;    
sayName();  // undefined

In this case, you can use the apply method to change the this pointer. The easiest way is to change the pointer directly, as follows:

// Continue the above case
sayName.apply(person);  // Mango

In order to ensure that the this of sayName is not lost, you can specify this through apply when you create it, as follows:

// closure is used
let sayName = (function (func, target) {
                    return function () {
                        return func.apply(target, arguments);
                    } 
                })(person.sayName, person);

// There is also a simpler way
let sayName = function () { 
    return person.sayName();
}

Aliasing can be abstracted into a function, and this can be specified to point to:

function methodAlias (func, target) {
    return function () {
        return func.apply(target, arguments);
    }
}

let sayName3 = methodAlias(person.sayName, person);
sayName3();

The same example also aliases functions such as document.querySelector as follows:

// Error: This method will use this pointing to document internally
let queryElem = document.querySelector;
queryElem('#id');   // Error

// Use a custom aliasing method, specifying that this points to
let queryElem = methodAlias(document.querySelector, document);
queryElem('#id');

use bind, recommended

The Function.prototype.bind method is implemented in most browsers and can be used as follows:

let sayName4 = person.sayName.bind(person);
sayName4();     // Mango

let queryElem = document.querySelector.bind(document);
queryElem('#id');

The implementation principle of the bind method is as follows:

Function.prototype.bind = function (context) {
    let self = this;
    return function () {
        self.apply(context, arguments);
    }
}

2. call and apply

2.1 Brief description

call differs from apply only in form, as follows:

const func = function (a, b, c) {
    console.log([a, b, c]);
}
// The second argument to apply is an array or array-like
func.apply(null, [1, 2, 3]);
// call will arrange the parameters of the function in order, and call is a syntactic sugar for apply
func.call(null, 1, 2, 3);
}

When the first parameter of call and apply is null, this in the body of the function (ordinary function and object method) will point to the default host object, which is window in the browser.

window.age = 200;
const obj = {
    age: 28,
    func2: function () {
        console.log('func2: ', this.age);
    }
}
const func1 = function () {
    console.log('func1: ', this.age);
}
func1.apply(null);  // 200 this points to window
func1.apply(obj);   // 28 this points to obj

obj.func2.apply(null);  // 200 this points to window
obj.func2.apply(obj);   // 28 this points to obj

2.2 Other uses of call and apply

Borrowing methods from other objects

The most typical is that the class array borrows the methods in Array.prototype.
For example, the parameter list arguments of a function is an array-like object. Although it also has a "subscript", it is not a real array, so you cannot use array methods like an array.
In this case, the methods in Array.prototype can be used.
In general, objects with "subscript" and length properties can use the array's prototype methods.

Common methods are as follows:

// Convert class array to array
Array.prototype.slice.apply(arguments);

// Add an element to an array-like
Array.prototype.push.apply(arguments, ['one']);

//...

Tags: Javascript Front-end

Posted by Rob the R on Thu, 19 May 2022 17:36:13 +0300