A brief introduction to this, bind, call & apply in JavaScript
this
is a rather special thing, basically it can be understood that the pointing this
of is the pointing of the nearest call, so this
it is also a confusing knowledge point in JS.
The this
previous basically uses the arrow function, because the arrow function does not contain bindings to this
, arguments
, super
or , so when using the arrow function, this
the direction of is easier to judge, but recently I feel that it is time to study this
, call
, andapply
.bind
The pointing problem of this
As mentioned above, this
the pointing to adopts the principle of nearest call, take the following code as an example:
const person = {
name: 'John',
greet() {
console.log(this);
console.log('Hello ' + this.name);
},
};
Under normal circumstances, the pointergreet
in should refer to , which is correct in the case of the following call:this
person
person.greet();
In this case, greet
it is called through person
this object, so greet
the binding environment is person
the object itself.
However, calling it in another way will have different results:
const {
greet } = person;
greet();
At this time, greet
the pointer becomes global, or becomes undefined in strict mode:
This is because greet
is deconstructed, and it is called under the global object at this time, in other words, at this time this
will be automatically bound to global
the object .
Another way to call:
const user = {
name: 'user',
};
user.greet = person.greet;
user.greet();
At this time, greet
because it is user
called, the bound object is user
:
In most cases this
, the problem is not very big, but sometimes it will be a troublesome thing to use callback, such as the following to simulate a mount callback and implement delayed execution:
const example = {
addEventListener(cb) {
this.cb = cb;
},
click() {
this.cb();
},
};
example.addEventListener(person.greet);
example.click();
At this time, the pointer greet
in becomes example
:
This method is also often implemented behind the scene, so the direction this
of is quite unclear.
In some cases, this is where bind
, call
and apply
can help.
bind
bind
will create a new function, and bind
will this
bind as the first passed parameter.
I think back to when I used React's class based component, when I didn't use the arrow function, I always used it bind
to execute:
class ExplainBindingsComponent extends Component {
constructor() {
super();
this.onClickMe = this.onClickMe.bind(this);
}
onClickMe() {
console.log(this);
}
}
This is to this
bind in the current class so that it does not follow the principle of proximity. The first parameter here is the object to be bound. It can be used when the first bound object does not matter null
. Use this
refers to the current object, or you can explicitly use an instantiated object.
In the above cases, the bind
binding rewriting method is as follows:
// 'use strict';
const person = {
name: 'John',
greet() {
console.log(this);
console.log('Hello ' + this.name);
},
};
const {
greet } = person;
greet();
greet.bind(person)();
const user = {
name: 'user',
};
user.greet = person.greet.bind(person);
user.greet();
const example = {
addEventListener(cb) {
this.cb = cb;
},
click() {
this.cb();
},
};
example.addEventListener(person.greet.bind(person));
example.click();
In this way, it can bind
be this
bound to a fixed object so that it is not affected by the proximity principle. In addition, bind
other parameters can also be accepted and passed to the called function, such as:
const person = {
name: 'John',
greet(from) {
console.log(this);
console.log('Hello ' + this.name + ' from ' + from);
},
};
const {
greet } = person;
// greet();
greet.bind(person, 'Sam')();
In some cases, using can bind
also simplify the code, for example, the following code can realize a basic addition, subtraction, multiplication and division operation:
const math = {
accumulated: 0,
add(num) {
const oldNum = this.accumulated;
this.accumulated += num;
console.log(`${
oldNum} + ${
num} = ${
this.accumulated}`);
},
substract(num) {
const oldNum = this.accumulated;
this.accumulated -= num;
console.log(`${
oldNum} - ${
num} = ${
this.accumulated}`);
},
multiply(num) {
const oldNum = this.accumulated;
this.accumulated *= num;
console.log(`${
oldNum} * ${
num} = ${
this.accumulated}`);
},
divide(num) {
const oldNum = this.accumulated;
this.accumulated /= num;
console.log(`${
oldNum} / ${
num} = ${
this.accumulated}`);
},
};
math.add(10);
math.substract(5);
math.multiply(2);
math.divide(5);
In this case, you can use bind
to simplify the operation:
const math = {
accumulated: 0,
calculation(operation, num) {
let oldNum = this.accumulated;
if (operation === '+') {
this.accumulated += num;
} else if (operation === '-') {
this.accumulated -= num;
} else if (operation === '*') {
this.accumulated *= num;
} else if (operation === '/') {
this.accumulated /= num;
}
console.log(`${
oldNum} ${
operation} ${
num} = ${
this.accumulated}`);
},
};
const {
calculation } = math;
calculation.bind(math, '+', 10)();
calculation.bind(math, '-', 5)();
calculation.bind(math, '*', 2)();
calculation.bind(math, '/', 5)();
The final running results are also consistent:
It does not mean that it must be used bind
to execute, but other methods can also be used to make the code more clear and readable, and improve reusability
call & apply
call
apply
and will not create a new function, but their usage is similar to bind, the first parameter is this
the pointer of , but call
can pass unlimited parameters, and apply
will accept an array like structure as the second parameter :
call(thisArg, arg1, /* …, */ argN);
apply(thisArg, argsArray);
According to mdn, under normal circumstances, when constructed is not involved, andbind
can be regarded as having the same effect:call
You can generally see
const boundFn = fn.bind(thisArg, arg1, arg2)
as being equivalent toconst boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs)
for the effect when it’s called (but not whenboundFn
is constructed).