Understand the mechanism of such a mysterious thing, the role of the first natural to deal with the interviewer, the role of the second is to maintain other people’s bad code ~
1. Pre-knowledge
1.1 A big misunderstanding of this
Many people have an unconscious misconception about this – that the value of this depends on where the function is declared
let obj = {
a: function () {
console.log(this);
},
b: function () {
let f = obj.a;
f();
}
}
obj.b(); // window
Copy the code
Many people will mistake this for obj when they see a function declared inside an object
1.2 Function names are Pointers
A function name is a pointer to a function object
As an object inside a function, this is naturally tied to the function
However, many people don’t realize that the function name is a pointer
Consider the following code:
- Whether fn1 can directly find the position of its function body (ans: yes)
- Is fn1 still related to OBJ?
function fn() {
console.log(this);
}
let obj = {
a() {
returnfn; }}let fn1 = obj.a();
fn1(); // window
Copy the code
If you can figure out the above two questions, you probably already have a feeling that this can refer to window
2 This mechanism is explained in detail
2.1 This is essentially the caller pointing to it
Javascript being an interpreted language, the value of this cannot be determined until the function is called.
What does that mean, like this code
function fn() {
console.log(this);
}
Copy the code
Do you know what this in FN is now?
Impossible to know, because fn is not called!
So what do you mean you have to know until the function is called?
Let’s see what happens when we make a different call to fn above
function fn() {
console.log(this);
}
let obj = {fn};
fn(); // window
obj.fn(); // obj
Copy the code
It is obvious that the value of this is different depending on how the call is made
**this essentially refers to its caller **.
Not so fast, the whole article is built around that quote
First, let’s analyze how many ways a function can be called, and then explain them separately:
The global call
The method call
The new call
2.1 Global Invocation (Independent Invocation)
Whenever ** is called independently, you can infer that this inside the function is Windows **
function foo() {
console.log(this);
}
foo(); // window
Copy the code
For foo(), foo is a function name, whereas in JS, a function name is just a pointer to the function name (), which stands alone. Javascript you Don’t Know is what the authors call a singlehander call, which makes this inside the called function bind to window by default
Combined with the statement that **this essentially refers to its caller, the essence of a global call is that window calls foo **
foo();
// Equivalent to the following
window.foo();
Copy the code
2.2 Method Invocation
Method (method)
Refers to an object whose property is a function, such asobj = {fn:function(){}}
And theobj.fn()
calledThe method call
Combine this with the statement **this essentially refers to its caller ** :
After a method call, this within the method points to the object that owns the method
let obj = {
fn() {
console.log(this);
}
}
obj.fn(); // obj
Copy the code
2.2.1 Proximity principle for method invocation
In the case of multiple nested objects, this refers to the object closest to which it was called
let obj = {
a: {
b: {
fn() {
console.log(this);
}
}
}
}
obj.a.b.fn(); // obj.a.b
Copy the code
2.2.2 Compare with global invocation
Here’s a code that, without running it, many people will guess wrong
let obj = {
a() {
console.log(this); }}let fn = obj.a;
obj.a(); // obj
fn(); // window
Copy the code
Obj. A (); No doubt, the key is why is fn() window
Fn is a pointer to obj. A. Fn () is a global call, so this refers to window
2.3 new
The key is to remember what **new does ** :
- Create a temporary object. This points to the temporary object
- The instance of
__proto__
Point to the prototype of the class- Return temporary object
function fn() {
console.log(this);
}
new fn(); // The result is shown in the screenshot below
Copy the code
3. This in other scenarios
3.1 This in strict mode
In strict mode, only one point is required; otherwise, the same as in non-strict mode
This is undefined in functions in the global scope
function test() {
"use strict"
console.log(this)
}
test() // undefined
Copy the code
So, when using a constructor, if you forget to add new, this no longer refers to the global object, but instead reports an error, because that is the global call to the function
let People = function (name) {
"use strict"
this.name = name
}
People() // Cannot set property 'name' of undefined
Copy the code
3.2 This in array
function fn() {
console.log(this)
}
arr[fn, fn2, fn3]
arr[0] ()// ??
// answer:arr
/ / parsing
// Arrays are also a type of object
// arr[0]() = arr.0().call(arr)
Copy the code
3.3 Nested functions this
Note that wherever the function name () appears, it is called independently
A / / examples
function fn0() {
function fn() {
console.log(this);
}
fn();
}
fn0(); // this is the global variable in fn
2 / / examples
let a = {
b: function () {
console.log(this) // {b:fn}
function xx() {
console.log(this) // window
}
xx()
}
}
a.b()
Copy the code
3.4 This in setTimeout and setInterval
This points to the global variable
document.addEventListener('click'.function (e) {
console.log(this);
setTimeout(function () {
console.log(this); // this is the global variable
}, 200);
}, false);
Copy the code
3.5 This in the event
The this in the event refers to the DOM node that triggered the event
document.querySelector('div').addEventListener('click'.function (e) {
console.log(this) // <div></div>
})
Copy the code
3.6 Arrow function does not contain this
Many people mistakenly think that this in the arrow function is bound to the parent scope, but this is not true
The essence of this in the arrow function is simply to use this directly from the parent scope; it has no this of its own
Therefore, the call/apply binding of arrow functions is also invalid
let test = () = > console.log(this);
test.call('this'); // window
test.apply('this'); // window
Copy the code
4 Specify this
Personally, I think it is best to use call/apply/bind to force binding for this
4.1 Overview of Call/Apply and Bind
- We are going to change our mind
call/apply
Fall into one category **,bind
Put them in a separate category - What all three have in common is that you can specify this
- Call /apply and bind are both tied to the prototype Function, so any instance of Function can call these three methods
Function.prototype.call(this,arg1,arg2)
Function.prototype.apply(this,[arg1,arg2])
Function.prototype.bind(this,arg1,arg2)
Copy the code
4.2 Call /apply — The first argument is this
4.2.1 Functions of Call/Apply
There is only one difference between call and apply: the call() method takes several arguments, while the apply() method takes an array of arguments
Function:
- Call a function
- Change the function’s this pointer
- Pass parameters to a function
Return value The return value is the return value of the function you called
window.a = 1
function print(b, c) {
console.log(this.a, b, c)
}
// call it independently
print(2.3) / / 1 2 3
// Use call and apply
print.call({a: -1}, -2, -3) // -1 -2 -3
print.apply({a: 0}, [...2, -3]) / / 0-2-3
Copy the code
4.2.2 Apply passes array parameters
Although apply passes an array of arguments, ** Apply actually breaks the array apart and passes it, so the function takes arguments from elements in the array, not an array
let fn = function () {
console.log(arguments)
}
fn.apply(null[1.2[3.4]]);
Copy the code
Therefore, one of the things that apply does frequently is to iterate over array elements into function arguments
Example a
Math.max() does not accept arrays, so finding the maximum value of a very long array can be cumbersome
We can use the apply method to pass the array to math.max ()
The essence is to unpack the array of arguments and pass it to math.max ()
let answer = Math.max.apply(null[2.4.3])
console.log(answer) / / 4
// Note the following three equivalents
Math.max.apply(null[2.4.3])
Math.max.call(null.2.4.3)
Math.max(2.4.3)
Copy the code
Example 2: Merge two arrays
It is worth noting that the ARR2 array is broken down into arguments
// Merge the second array into the first array
'celery', 'beetroot');
let arr1 = ['parsnip'.'potato']
let arr2 = ['celery'.'beetroot']
arr1.push.apply(arr1, arr2)
// Attention!! This means to specify that the push method is called
// So when this = arr1
// arr1 calls push
// if ('celery', 'beetroot')
console.log(arr1)
// ['parsnip', 'potato', 'celery', 'beetroot']
Copy the code
Of course, when using Apply, be careful not to bind the pointer to this; otherwise, an error may be reported
Math.max.apply(null[2.4.3]) // It works perfectly
arr1.push.apply(null, arr2) Uncaught TypeError: array.prototype. Push called on null or undefined
/ / that
Math = {
max: function (values) {
// This is not used}}Array.prototype.push = function (items) {
// This -> The array itself that calls push
// If this is null, push into null and an error will be reported
// Array.prototype.push called on null or undefined
}
// The following three values are completely equivalent, since this is already arr1
Array.prototype.push.apply(arr1, arr2)
arr1.push.apply(arr1, arr2)
arr2.push.apply(arr1, arr2)
Copy the code
Small holdings test
function xx() {
console.log(this)
}
xx.call('1') // ??
xx() // ??
Copy the code
4.3 the bind
let newfun = fun.bind(thisArg[, arg1[, arg2[, ...]]])
Copy the code
This role
Note that bind has no effect on the original function
- Create a new function that is a copy of the original function
- Change the this and argument of the new function
- Return this new function
4.3.2 Binding function and objective function
The function bind() returns a new function instead of the target function.
I personally prefer to distinguish between new and original functions, because the more new nouns there are, the more difficult it is to understand
So what happens when a new function is called? Remember that the following statement is equivalent to calling/applying the call/apply function and specifying that you pass this
function xx() {
console.log(this)}let foo = xx.bind({'name':'jason'})
// foo -- new function
// xx -- function
foo()
// When a new function is called, the operation on the original function is as follows (pseudocode)
function foo(){
xx.call({'name':'jason'})}Copy the code
4.3.3 the bind () parameter
- bind(this,arg1,arg2…) will
arg1,arg2...
Inserted into theThe new functionThe binding functionThe arguments of theThe starting position
- Calling a new function, the parameter passed again will only follow
arg1,arg2...
behind
function list() {
// The target function is:
return Array.prototype.slice.call(arguments);
}
// new function
let leadingThirtysevenList = list.bind(null.37.38);
let newList1 = leadingThirtysevenList();
let newList2 = leadingThirtysevenList(1.2.3);
let newList3 = leadingThirtysevenList(-1, -2);
console.log(newList1) // [37, 38]
console.log(newList2) // [37, 38, 1, 2, 3]
console.log(newList3) // [37, 38, -1, -2]
Copy the code
4.3.4 Natively implement a bind using this + call/apply
Thinking process
Implementing BIND is all about implementing bind
- The first argument to bind is this
- Bind can return a new function that calls the original function and can specify its this, as well as accept arguments
- The arguments passed by the new function come after those passed by bind
code
Function.prototype._bind = function () {
// bind specifies this
let bindThis = arguments[0]
// The argument passed by bind
let bindArgs = Array.prototype.slice.call(arguments.1)
// this refers to the old function that called _bind
let oldFunction = this
// Return a new function
return function () {
// The new function is the same as the old function, but it only calls the old function with apply and then returns the value of the old function
// Convert arguments to arrays for the new function
let newArgs = Array.prototype.slice.call(arguments.0)
// merge bindArgs and newArgs, and pass newArgs after bindArgs to the old function
return oldFunction.apply(bindThis, bindArgs.concat(newArgs))
}
}
/ / test
function fn() {
console.log(arguments)}let newFn = fn._bind(null.1.2);
newFn(4.6)
Copy the code