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']); //...