I believe that this in Javascript will make many students confused in work and study. The author also does the same. After reading various materials and practical work applications, the author makes the following sorting out, mainly including people’s wrong understanding of this for a long time and the binding rules of this. Arrow function, the actual work scene encountered problems, I hope to have this confusion you can help.
Two kinds of misconceptions
Point to their own
The first misconception about this is that it is easy to interpret this as referring to the function itself. In fact, the direction of this is not determined at the function definition stage. Only when the function is executed can we determine who this refers to.
In the following example, declare the function foo() and add an attribute count to function object foo when foo. Count =0. But the this in function foo’s this.count code does not refer to that function object. Foo (I) in the for loop uses its object as window, equivalent to window.foo(I), So this in foo refers to window.
function foo(num){
this.count++; // Records how many times foo is called
}
foo.count = 0;
window.count = 0;
for(let i=0; i<10; i++){
if(i > 5) {
foo(i);
}
}
console.log(foo.count, window.count); / / 0. 4
Copy the code
Point to the scope of the function
The second misconception about this is that this refers to the scope of a function
The following code attempts to call the bar function in foo, depending on the context.
-
Browser: In the browser environment, there is no problem. The globally declared function is placed under the window object, and the this generation in foo refers to the window object. In the global environment, the variable a is not declared, so this. The output is undefined.
-
Node.js: In node. js, the declared function will not be placed under the global object, so calling this.bar in foo will raise TypeError: This. Bar is not a function error. To run error-free, omit the preceding this when calling the bar function.
function foo(){
var a = 2;
this.bar();
}
function bar(){
console.log(this.a);
}
foo();
Copy the code
This four binding rules
The default binding
When a function call is a stand-alone call (without a function reference) that cannot invoke other binding rules, we call it a “default binding”, binding to global objects in non-strict mode, and binding to undefined in use strict mode.
Strict mode call down
'use strict'
function demo(){
// TypeError: Cannot read property 'a' of undefined
console.log(this.a);
}
const a = 1;
demo();
Copy the code
Non-strict mode call down
Binding a to window.a in the browser environment, the following code uses var to declare variable A to print 1.
function demo(){
console.log(this.a); / / 1
}
var a = 1;
demo();
Copy the code
The following code uses let or const to declare variable a and outputs undefined
function demo(){
console.log(this.a); // undefined
}
let a = 1;
demo();
Copy the code
The default binding of this is intended to be emphasized in the examples, but you will notice that the above two types of code have different results because they use var and let declarations respectively. The reason for this is related to the concept of top-level objects
On the Issue: Nodejs-roadmap /issues/11 introduces the concept of the top-level object. The attributes of the top-level object (the browser environment refers to window, the Node.js environment refers to Global) and the assignment of Global variable attributes are equivalent. Var and function are used to declare top-level object properties, while let belongs to ES6 specification. However, global variables declared in ES6 specification, such as let, const, and class, do not belong to top-level object properties.
Implicit binding
Is contained by an object at the call location of a function and has a context, as shown in the following example:
function child() {
console.log(this.name);
}
let parent = {
name: 'zhangsan'.
child,
}
parent.child(); // zhangsan
Copy the code
The function is called using the parent object context to refer to the child function.
The pitfalls of implicit binding
Implicitly bound functions, because some careless operation will lose the bound object, will apply the default binding from the original binding rule, look at the following code:
function child() {
console.log(this.name);
}
let parent = {
name: 'zhangsan'.
child,
}
let parent2 = parent.child;
var name = 'lisi';
parent2();
Copy the code
Assigning the parent. Child function itself to parent2, calling parent2() is actually an undecorated function call, so the default binding is applied.
According to the binding
Explicit binding and implicit binding are literally opposite, with one being more direct and the other more euphemistic. Here’s what the two rules mean:
-
Implicit binding: Bind this implicitly to the function referred to by an attribute inside an object (for example, function child(){}).
-
Show binding: Enforce binding calls when you need to reference an object. Js provides call(), apply() and the built-in function.prototype.bind method in ES5.
Call () and apply() both set this as their first arguments. The difference is that apply passes the arguments as arrays, while call passes them one by one.
function fruit(. args){
console.log(this.name, args);
}
var apple = {
name: 'apple'
}
var banana = {
name: 'banana'
}
fruit.call(banana, 'a'.'b') // [ 'a', 'b' ]
fruit.apply(apple, ['a'.'b']) // [ 'a', 'b' ]
Copy the code
The following is an example of the bind bind binding, which simply binds a value to the function this and returns the bound function. Output is only given when the fruit function is executed, as in:
function fruit(){
console.log(this.name);
}
var apple = {
name: 'apple'
}
fruit = fruit.bind(apple);
fruit(); / / apple
Copy the code
Call, apply, and bind can also be used in a context, for example:
function fruit(name){
console.log(`The ${this.name}: ${name}`);
}
const obj = {
name: 'This is fruit'.
}
const arr = ['apple'.'banana'];
arr.forEach(fruit, obj);
// This is fruit: apple
// This is fruit: bananas
Copy the code
The new binding
The new binding can also affect this call, which is a constructor, and each new binding creates a new object.
function Fruit(name){
this.name = name;
}
const f1 = new Fruit('apple');
const f2 = new Fruit('banana');
console.log(f1.name, f2.name); // apple banana
Copy the code
priority
This has precedence if more than one binding rule is applied to the place where it is called: new binding -> display binding -> Implicit binding -> default binding.
Arrow function
Arrow functions are not defined using the function keyword and do not use the four standard specifications for this described above. Arrow functions inherit from the this binding of the outer function call.
When fruit.call(apple) is executed, the arrow function this is bound and cannot be modified again.
function fruit(){
return (a)= > {
console.log(this.name);
}
}
var apple = {
name: 'apple'
}
var banana = {
name: 'banana'
}
var fruitCall = fruit.call(apple);
fruitCall.call(banana); / / apple
Copy the code
Several pits in use of This
Simulate classes through functions and prototype chains
In the following example, you define the function Fruit, then you define the info method on the prototype chain, instantiate the object F1 and define the object F2 to call the info method, respectively.
function Fruit(name) {
this.name = name;
}
Fruit.prototype.info = function() {
console.log(this.name);
}
const f1 = new Fruit('Apple');
f1.info();
const f2 = { name: 'Banana' };
f2.info = f1.info;
f2.info()
Copy the code
After the output, the result is different because the this in the info method corresponds not to the context in which it was defined, but to the context in which it was called, according to the implicit binding rules we discussed above.
Apple
Banana
Copy the code
Use arrow functions on the prototype chain
If you use constructors and prototype chains to emulate classes, you cannot define arrow functions on the prototype chain because the this in the arrow function inherits the this binding of the outer function call.
function Fruit(name) {
this.name = name;
}
Fruit.prototype.info = (a)= > {
console.log(this.name);
}
var name = 'Banana'
const f1 = new Fruit('Apple');
f1.info();
Copy the code
Use in events
As an example of node.js, when our listener is called, this will point to an EventEmitter instance attached to the listener if we declare a normal function. This will not point to an EventEmitter instance if we use the arrow function.
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
constructor() {
super(a);
this.name = 'myEmitter';
}
}
const func1 = (a)= > console.log(this.name);
const func2 = function () { console.log(this.name); };
const myEmitter = new MyEmitter();
myEmitter.on('event', func1); // undefined
myEmitter.on('event', func2); // myEmitter
myEmitter.emit('event');
Copy the code
This may have more questions than the ones listed above, feel free to comment in the comments section if you have any other questions.
Reference
- JavaScript you Don’t Know (Volume 1)
About the author: May Jun, software designer, author of the public account “Nodejs Technology Stack”.
– END –