What are the pointer functions that change the this pointer inside a function and what are their differences?
What are the directions for this?
Code Analysis Questions:
var name = 'window' var person1 = { name: 'person1', show1: function () { console.log(this.name) }, show2: () => console.log(this.name), show3: function () { return function () { console.log(this.name) } }, show4: function () { return () => console.log(this.name) } } var person2 = { name: 'person2' } person1.show1() person1.show1.call(person2) person1.show2() person1.show2.call(person2) person1.show3()() person1.show3().call(person2) person1.show3.call(person2)() person1.show4()() person1.show4().call(person2) person1.show4.call(person2)()
1. Direction of this
Baidu and Google input the keyword "this direction". There must be thousands of articles. It is not necessary to read all the articles in order to master them in all aspects and without dead ends. So it's better to comb out a solid frame and fill it with our thoughts.
Mind Mapping
The essence of this section:
this always points to an object (in non-strict mode) and specifies which object is dynamically bound at runtime based on the execution environment of the function, not when the function is declared;
In addition to the less commonly used cases of with and eval, this can be roughly divided into four types for practical use:
Method calls as objects;
Called as a normal function;
Constructor call;
Call or apply call;
In the arrow function, this points to this in the upper scope of the function.
The difference between a constructor and a normal function is how it is called.
A, call(B) => can be understood as calling method A within the scope of B;
Analysis
1. Method Calls as Objects
this points to a function when its method as an object is called
var obj = { a: 'yuguang', getName: function(){ console.log(this === obj); console.log(this.a); } }; obj.getName(); // true yuguang
2. Call as a normal function
When a function is called not as a property of an object, but as a normal function, this always points to a global object (usually a Window object in a browser)
window.name = 'yuguang'; var getName = function(){ console.log(this.name); }; getName(); // yuguang
Or this confusing code:
window.name = 'King' var obj = { name: 'yuguang', getName: function(){ console.log(this.name); } }; var getNew = obj.getName; getNew(); // King
In strict mode in ES5, this is specified not to point to global objects, but to undefined
3. Constructor Calls
With the exception of some built-in functions, most functions in Js can be constructors, and they are no different from normal functions
The difference between a constructor and a normal function is how it is called:
When the new operator calls a function, it always returns an object, which this usually points to
var MyClass = function(){ this.name = 'yuguang'; } var obj = new MyClass(); obj.name; // yuguang
However, if an object object is explicitly returned, the result of this operation will eventually return that object.
var MyClass = function () { this.name = 1; return { name: 2 } } var myClass = new MyClass(); console.log('myClass:', myClass); // { name: 2}
This is not an issue as long as the constructor returns no data or data of a non-object type.
4. call or apply calls
call and apply can dynamically change this of a function compared to normal function calls
var obj1 = { name: 1, getName: function (num = '') { return this.name + num; } }; var obj2 = { name: 2, }; // It can be understood that obj1 was called in the scope of obj2. GetName() function console.log(obj1.getName()); // 1 console.log(obj1.getName.call(obj2, 2)); // 2 + 2 = 4 console.log(obj1.getName.apply(obj2, [2])); // 2 + 2 = 4
5. Arrow function
The arrow function does not create its own this, it only inherits it from the upper level of its scope chain.
Therefore, in the following code, this value in the function passed to setInterval is the same as that in the closed function:
this.val = 2; var obj = { val: 1, getVal: () => { console.log(this.val); } } obj.getVal(); // 2
Common pits
Like a title, sometimes this points to undefined
Situation One
var obj = { name: '1', getName: function (params) { console.log(this.name) } }; obj.getName(); var getName2 = obj.getName; getName2();
At this point, when getName2() is called as a normal function, this points to the global object, window.
Situation 2
When we want to simplify our code by encapsulating the Dom method ourselves:
var getDomById = function (id) { return document.getElementById(id); }; getDomById('div1') //dom node
So let's see if that's okay?
var getDomById = document.getElementById getDomById('div1') // Uncaught TypeError: Illegal invocation
This is because:
When we call the method of the document object, this inside the method points to the document.
When we use getId to apply a method within a document and call it as a normal function, the this of the function content points to the global object.
Use call and apply to fix scenario two
document.getElementById = (function (func) { return function(){ return func.call(document, ...arguments) } })(document.getElementById) // Save document in scope with immediate execution function
2. call and apply
Don't resist it because of its "power". Understand and become familiar with it is something we have to do, reluctantly!
Mind Mapping
1. The difference between call and apply
Let's start with the difference, because they are almost identical, so the code instances call and apply below can be easily switched.
When they are designed to do the same thing, the only difference is that the format of the arguments is different
apply accepts two parameters
The first parameter specifies the direction of the this object within the function body
The second parameter is a subscripted set of parameters (which can be an array or an array of classes)
call accepts parameters that are not fixed
The first parameter specifies the direction of the this object within the function body
The second and subsequent parameters for the function call
Because in all (non-arrow) functions, the parameters of the function can be referenced in the function through the arguments object. This object contains every parameter passed to the function and is itself an array of classes, which we apply use more often.
Calls are grammatical sugars wrapped on apply, so if we know the number of parameters explicitly and want to show them, we can use call.
When using call or apply, if the first parameter we pass in is null, this in the function body will point to the host object by default, and window in the browser.
Method of borrowing other objects
We can pass null directly instead of any object
Math.max.apply(null, [1, 2, 3, 4, 5])
2. What can call and apply do?
Call a function with a specified this value and one or more parameters given separately -- MDN
Call the constructor to implement inheritance;
this calls the function and specifies the context;
Call the function without specifying the first parameter;
1. Call the constructor to implement inheritance
To achieve the effect of inheritance by borrowing:
function Product(name, price) { this.name = name; this.price = price; } function Food(name, price) { Product.call(this, name, price); // this.category = food; } var hotDog = new Food('hotDog', 20);
2. Call the function and specify this context
this is pointed at obj
function showName() { console.log(this.id + ':' + this.name); }; var obj = { id: 1, name: 'yuguang' }; showName.call(obj)
3. Simple call to a function using call
Math.max.apply(null, [1,2,3,10,4,5]); // 10
3. Implement a call by simulation
Let's see what call s do to help us?
var foo = { value: 1 }; function show() { console.log(this.value); }; show.call(foo); //1
Just like solving equations, to find a breakthrough in a known condition:
call changes the direction of this, pointing to foo;
The show function is executed;
The parameter passed in should be this +parameter list;
First Edition Code
Of the three points mentioned above, only one has been completed, and the parameters passed in
var foo = { value: 1 }; function show() { console.log(this.value); }; Function.prototype.setCall = function (obj) { console.log(this); // this point to show obj.func = this; // To make a function an internal property of an object obj.func(obj.value); // Specify function delete obj.func // Delete the function, when nothing happens~ } show.setCall(foo);
Second edition code
To solve the parameter problem, we need to be able to get the parameter and pass it in correctly:
var foo = { value: 1 }; function show(a, b) { console.log(this.value); console.log(a + b); }; Function.prototype.setCall = function (obj) { obj.fn = this; // To make a function an internal property of an object var args = []; for(let i = 1; i < arguments.length; i++){ args.push('arguments[' + i + ']'); } eval('obj.fn(' + args + ')'); // Incoming parameters delete obj.fn; // Delete the function, when nothing happens~ } show.setCall(foo, 1, 2); // 1 3
At this point, we can do that, using call with multiple parameters passed in, but if you only want to use one method?
Third edition code
Function.prototype.setCall = function (obj) { var obj = obj || window; obj.fn = this; var args = []; for(var i = 1, len = arguments.length; i < len; i++) { args.push('arguments[' + i + ']'); } var result = eval('obj.fn(' + args +')'); delete obj.fn; return result; }; // Test it var value = 2; var obj = { value: 1 }; function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age } } bar.setCall(null); // 2 console.log(bar.setCall(obj, 'yuguang', 18));
IV. Bid
The bind() method creates a new function whose this is specified as the first parameter of bind() when bind() is called, while the rest of the parameters are used as the parameters of the new function for the call - MDN
When you mention call and apply, you can't get around bind. Let's try to simulate a bind method to get a better understanding of it:
Function.prototype.bind = function (obj) { var _this = this; // Save functions that call bind var obj = obj || window; // Determine which this is being pointed to, and if obj is empty, this execution scope needs top return function(){ return _this.apply(obj, arguments); // Correct this's direction } }; var obj = { name: 1, getName: function(){ console.log(this.name) } }; var func = function(){ console.log(this.name); }.bind(obj); func(); // 1