This binding rule
Series of the opening
Establish clear, precise, essential concepts and clear, precise, necessary connections between them as you enter the front end, so that you can stay calm in whatever interview you’re going to be doing. There is no catalog, but a web of knowledge formed through the association of concepts, as you can see below. When you encounter a relation concept, you can use the parentheses (strong/weak) to determine whether the relation is strongly related to the concept you are understanding (you need to understand before you can proceed) or weakly related (knowledge expansion) to improve your reading efficiency. I also update related concepts regularly.
The interview questions
- Tell me what you know about this direction
- Do you know this for the arrow function
- Call /apply/bind/new is associated with this
- Can you implement the above primitive functions and explain how they work
What’s this for? Why?
In the last article we introduced what this is and briefly explained how to analyze this pointing.
The summary is as follows:
- This is a keyword in javascript that provides a more elegant way to implicitly “pass” an object reference, so apis can be designed to be cleaner and easier to reuse.
this
Is in theBind at run timeandNot binding at authoring timeIts context depends on the conditions under which the function is called.this
The binding has nothing to do with where the function is declared, but only how the function is called.- When a function is called, an execution context is created. This record contains information about where the function was called (call stack, execution stack), how the function was called, the parameters passed in, and so on. This is one of the properties of the record that is used during the execution of the function. One part of the creation process for the execution context is this Binding.
- This is actually a binding that happens when the function is called, and what it points to depends entirely on where and how the function is called.
- Alternatively, we can print a stack trace with console.trace().
Remember there are some ways you can’t read it and skip it, it’s not linear, you can write down what’s not clear, look at the next one, the unknown is always there, don’t be afraid.
Let’s look at the various binding rules in detail
1. Default binding
Independent function calls. You can think of this rule as the default rule when no other rule can be applied.
If strict mode is used, the default binding is not available for global objects, so this is bound to undefined
function foo() {
console.log( this.a );
}
var a = 2;
foo(); / / 2
Copy the code
This is using the default binding this points to the global object and the browser is window; Node is global or {}
Foo () is called directly with an undecorated function reference, so only the default binding can be used and no other rules can be applied.
2. Implicit binding
This rule focuses on the following key points.
- 2.1 Whether there is a context object at the call location
When a function references a context object, the implicit binding rule binds this in the function call to the context object to observe the code below
function foo() {
console.log(this.a);
}
var obj = {
a: 2.foo: foo
};
obj.foo(); / / 2
Copy the code
Because this is bound to obj when foo() is called, this.a and obj.a are the same.
- 2.2 Only the last layer in the object attribute reference chain will affect the call location as shown in the following example
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42.foo: foo
};
var obj1 = {
a: 2.obj2: obj2
};
obj1.obj2.foo(); / / 42
Copy the code
- 2.3 Implicit Loss
One of the most common problems with this binding is that implicitly bound functions lose the binding object, that is, they apply the default binding to bind this to a global object or undefined.
function foo() {
console.log(this.a);
}
var obj = {
a: 2.foo: foo
};
var bar = obj.foo; // Function alias!
var a = "global"; // a is an attribute of the global object
bar(); // "global"
Copy the code
Although bar is a reference to obj.foo, it actually refers to the function foo itself because of stack and heap memory, so bar() is actually a function call without any decoration, so the default binding is applied.
- 2.3 Common problems with callback functions missing this binding
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// fn actually refers to foo
fn(); // <-- call location!
}
var obj = {
a: 2.foo: foo
};
var a = "global"; // a is an attribute of the global object
doFoo(obj.foo); // "global"= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =A similar problem occurs with callbacks such as setTimeout()
function setTimeout(fn, delay) {
// Wait delay milliseconds
fn(); // <-- call location!
}
Copy the code
Passing function references as formal parameters is a common way to call back functions.
3. Explicit binding
As we have just seen, when analyzing implicit binding, we must bind this indirectly (implicitly) to an object by including a property that points to a function inside the object and by referring to the function indirectly through this property.
So what if we want to force a function call on an object instead of including a function reference inside the object?
Specifically, you can use the call(..) of the function. And the apply (..) Methods How do these two methods work? Their first argument is an object, which they bind to this, which they then specify when calling the function. Because you can specify the binding object for this directly, we call it an explicit binding.
function foo() {
console.log(this.a);
}
var obj = {
a: 2
};
foo.call(obj); / / 2
Copy the code
Through the foo. Call (..) , we can force foo to bind its this to obj when calling foo. Unfortunately, explicit binding still doesn’t solve the missing binding problem we mentioned earlier.
The following two solutions are available:
- 3.1 hard binding
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
var bar = function() {
foo.call( obj );
};
bar(); / / 2
setTimeout( bar, 100 ); / / 2
// A hard-bound bar can no longer modify its this
bar.call( window ); / / 2
Copy the code
We create the function bar() and manually call foo.call(obj) inside it to force foo’s this to be bound to obj. So you can’t use it againcall
Change its this.
A typical use of hard binding is to create a wrapped function that passes in all the arguments and returns all the values it receives:
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
var obj = {
a: 2
};
var bar = function() {
return foo.apply(obj, arguments);
};
var b = bar(3); // 2 3 a is the a of obj, 3 is the argument passed in
console.log( b ); / / 5
Copy the code
Because hard binding is a very common pattern, the built-in function.prototype. bind method is provided in ES5.
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
var obj = {
a: 2
};
var bar = foo.bind(obj);
var b = bar(3); / / 2, 3
console.log( b ); / / 5
Copy the code
bind(..) Returns a new hard-coded function that sets the argument to the context of this and calls the original function. This enables hard binding.
- 3.2 “Context” of API Calls
Many of the built-in JS functions provide an optional argument, called a context, which can be used as a bind(..). Again, make sure the callback uses the specified this.
These functions actually pass call(..) And the apply (..) Explicit binding is implemented.
Note that “context” is not just a name for an execution context.
4. The new binding
The mechanics of new in JavaScript are actually quite different from class-oriented languages. Let’s start by redefining “constructors” in JavaScript.
In JavaScript, constructors are simply functions that are called when the new operator is used. They do not belong to a class, nor do they instantiate a class. In fact, they are not even a special type of function; they are just ordinary functions called by the new operator.
When a function is called with new, or when a constructor call occurs, the following operations are performed automatically.
- Create an empty simple JavaScript object (that is {});
- The new object will be executed to [[prototype]] join the concept. Or linking the object (setting its constructor) to another object;
- Step 1 Use the newly created object as the context for this
- If the function returns no other object, the function call in the new expression automatically returns the new object.
See the following example:
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); / / 2
Copy the code
Call foo(..) with new , we construct a new object and bind it to foo(..). Call to this. New is the last way to influence the behavior of the this binding when a function is called, and we call it the new binding.
This binding priority
There are four binding rules mentioned above. Here are the priorities for each of them. The key points are the following
1. There is no doubt that the default binding has the lowest priority of the four rules
2. Implicit binding vs. explicit binding => Explicit binding has a higher priority
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2.foo: foo
};
var obj2 = {
a: 3.foo: foo
};
obj1.foo(); / / 2
obj2.foo(); / / 3
obj1.foo.call(obj2); // 3 Explicit binding has a higher priority
obj2.foo.call(obj1); // 2 Explicit binding has a higher priority
Copy the code
3. New binding vs. implicit binding => New binding has a higher priority
function foo(something) { // STH is short for something
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo(2); // this refers to obj1 this.a = STH = obj.a = 2
console.log(obj1.a); / / 2
obj1.foo.call(obj2, 3); // displays binding this to obj2 and passing in parameter 3
console.log(obj2.a); // 3 Due to display > implicit this.a = STH equivalent to obj2.a = 3
var bar = new obj1.foo(4); // A new call to this points to the returned new object bar
console.log( bar.a ); // 4 this.a = STH = bar.a = 4
Copy the code
4. New binding vs. explicit binding
New and call/apply cannot be used together and therefore cannot be tested directly with new foo.call(obj1). But we can use hard binding to test their priority.
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind(obj1); // Hard bind (to obj1)
bar(2);
console.log(obj1.a); / / 2
var baz = new bar(3);
console.log(obj1.a); / / 2
console.log( baz.a ); // 3 new modified the hard binding (to obj1) call bar(..) In this
Copy the code
In simple terms, this code in bind determines if the hard-bound function is called by new, and if so, replaces the hard-bound this with the newly created this.
summary
This can be judged in the following order:
-
Is the function called in new (new binding)? If so, this binds to the newly created object. Var bar = new foo() this points to bar
-
Is the function called by call, apply(explicit binding), or hard binding? If so, this binds to the specified object. var bar = foo.call(obj2)
-
Is the function called in a context object (implicitly bound)? If so, this binds to the upper context object. var bar = obj1.foo()
-
If neither, use the default binding. If in strict mode, it is bound to undefined, otherwise it is bound to global objects. var bar = foo()
Bind the exception
- If you pass null or undefined as a binding object for this to call, apply, or bind, these values will be ignored when called, and default binding rules such as:
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); / / 2
Copy the code
- Indirect reference
It is possible (intentionally or unintentionally) to create an “indirect reference” to a function, in which case the default binding rules apply to calling the function. Such as
function foo() {
console.log(this.a);
}
var a = 2;
var o = { a: 3.foo: foo };
var p = { a: 4 };
o.foo(); / / 3
(p.foo = o.foo)(); / / 2
Copy the code
The return value of the assignment expression p.foo = o.foo is a reference to the target function, so the call location is foo() rather than p.foo() or o.foo(). As we said earlier, the default binding is applied here.
- Soft binding
As we’ve seen before, hard binding is a way to force this to the specified object (except when using new), preventing function calls from applying the default binding rules.
The problem is that hard binding greatly reduces the flexibility of the function and makes it impossible to modify this using either implicit or explicit binding.
If you can give the default binding a value other than a global object and undefined, you can achieve the same effect as hard binding, while preserving the ability of implicit or explicit binding to modify this. This is soft binding.
This lexical arrow function
This is also one of the binding exceptions
The arrow function is not just a “shortcut” to writing clean code. It also has very special and useful properties.
JavaScript is full of situations where we need to write small functions that execute elsewhere. Such as:
- Arr.foreach (func) — forEach performs func on each array element.
- SetTimeout (func) — func is executed by the built-in scheduler.
The essence of JavaScript is to create a function and pass it somewhere. In such functions, we usually don’t want to leave the current context. This is where the arrow function comes in.
Arrow functions don’t have this. If you access this, you get it externally.
let group = {
title: "Our Group".students: ["John"."Pete"."Alice"].showList() {
this.students.forEach(
student= > alert(this.title + ':'+ student) ); }}; group.showList();Copy the code
The arrow function is used in forEach, so this.title is exactly the same as the external method showList. That is: group.title.
So we need to pay attention to a couple of things
- Arrow functions cannot be new. The absence of this naturally implies another limitation: arrow functions cannot be used as constructors. You cannot call them with new.
- Arrow functions also don’t have arguments variables.
- Arrow functions are for short code that has no “context” of its own, but works in the current context. As in a callback function, such as an event handler or timer
conclusion
If you want to determine the this binding of a running function, you need to find where the function is called directly. Once found, we can apply the following four rules in sequence to determine the object bound to this.
-
Called by new? Bind to the newly created object.
-
Called by call or apply(or bind)? Binds to the specified object.
-
Called by a context object? Bind to that context object.
-
Default: bind to undefined in strict mode, otherwise bind to global object.
-
Instead of using the four standard binding rules, the arrow function in ES6 determines this based on the current lexical scope. Specifically, the arrow function inherits the this binding of the outer function call (whatever this is bound to).
How does it work?
After we understand the concept, to explore its implementation under the (interview) are often asked about the source code, can someone feel futile, I think it is useful for developing the other associated concepts, will know 】 【 can also take a look at your hard coding ability, to assemble to see how your memory is good. (^ – ^)
Bind /call/apply/new
Do you often write interviews? Method: actually, I think is on the basis of the definition of a deep understanding to write, although some students know how to implement these methods, but the concept of fuzzy, even if is clear definition, say, can understand truly understand and apply to your development, writing principle implementation code is not the goal. You have a good understanding of the definition, and even if you haven’t read the implementation article before, it’s better to follow the interviewer’s tips and explain what you know about the concept and use your coding skills to implement it in the field.
Because the implementation here is long, it is better to write a single article. Here are the links of MDN methods:
-
The bind method creates a new function. When bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments are used as arguments to the new function.
-
The call method calls a function with a specified this value and one or more arguments given separately.
-
The apply apply() method calls a function with a given this value and arguments supplied as an array (or array-like object).
Note: The call() method works like apply() except that call() takes a list of parameters, while apply() takes an array of parameters.
- The new operator creates an instance of a user-defined object type or of a built-in object with a constructor
Bind /call/apply/new
reference
- You don’t know Javascript
- developer.mozilla.org/zh
- Github.com/yygmind/blo…
- Useful. Javascript. The info/arrow – funct…