Part 10 of the JavaScript In-depth series reveals the truth about how call and apply change this, using call and apply simulations
call
One sentence introduction:
The call() method calls a function or method using a specified value of this and several specified parameter values.
Here’s an example:
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); / / 1Copy the code
Two points to note:
- Call changes this to point to foo
- The bar function executes
Simulate the first step
So how do we simulate these two effects?
Imagine that when a call is called, the foo object is changed to look like this:
var foo = {
value: 1.bar: function() {
console.log(this.value)
}
};
foo.bar(); / / 1Copy the code
This is pointing to foo, isn’t that simple?
But that adds a property to the foo object itself, which doesn’t work!
But don’t worry, we can delete it with delete
Therefore, the steps of our simulation can be divided into:
- Sets a function as a property of an object
- Execute the function
- Delete this function
Here’s an example:
/ / the first step
foo.fn = bar
/ / the second step
foo.fn()
/ / the third step
delete foo.fnCopy the code
Fn is the property name of the object, and I’m going to delete it anyway, so it doesn’t matter what it is.
With this in mind, we can try to write the first version of the call2 function:
/ / the first edition
Function.prototype.call2 = function(context) {
// We first need to get the function that called the call, which we can use this to get
context.fn = this;
context.fn();
delete context.fn;
}
// Test
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call2(foo); / / 1Copy the code
Just can print 1 ah! Is not very happy! (~ ~ ▽ ~) ~
Simulate the second step
As we mentioned in the beginning, the call function can also execute the function with given arguments. Here’s an example:
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo, 'kevin'.18);
// kevin
/ / 18
/ / 1Copy the code
Note: The parameters passed in are uncertain. What can I do?
We can take the Arguments from the Arguments object, take the second and last Arguments, and put them in an array.
Like this:
// Arguments for this example, where arguments are:
// arguments = {
// 0: foo,
// 1: 'kevin',
/ / 2:18,
// length: 3
// }
// Since Arguments is an array-like object, you can use the for loop
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + '] ');
}
// args = [foo, 'Kevin ', 18]Copy the code
With the variable length argument problem solved, we are now going to put the argument array into the arguments of the function we want to execute.
// Put the elements of the array as multiple arguments to the function parameters
context.fn(args.join(', '))
// (O_o)??
// This method must not work!!Copy the code
If you want to simulate an ES3 method, you need to use the ES6 method. Well, that’s fine. But this time we use the eval method to spell out a function, something like this:
eval('context.fn(' + args +') ')Copy the code
Here, args will automatically call array.toString ().
So our second release overcame two big problems, and the code looks like this:
/ / the second edition
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + '] ');
}
eval('context.fn(' + args +') ');
delete context.fn;
}
// Test
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'kevin'.18);
// kevin
/ / 18
/ / 1Copy the code
(๑ • ̀ ㅂ ́) organisation ✧
Simulate the third step
The simulation code is 80% complete, but there are two small points to note:
1. This parameter can be passed null. When null, it is regarded as pointing to window
Here’s an example:
var value = 1;
function bar() {
console.log(this.value);
}
bar.call(null); / / 1Copy the code
Although this example itself does not use call, the result is the same.
2. Functions can return values!
Here’s an example:
var obj = {
value: 1
}
function bar(name, age) {
return {
value: this.value,
name: name,
age: age
}
}
console.log(bar.call(obj, 'kevin'.18));
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }Copy the code
But that’s fine, so let’s go straight to the third and final version of the code:
/ / the third edition
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + '] ');
}
var result = eval('context.fn(' + args +') ');
delete context.fn
return result;
}
// Test
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call(null); / / 2
console.log(bar.call2(obj, 'kevin'.18));
/ / 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }Copy the code
So far, we have completed the simulation of call and give ourselves a thumbs up B ( ̄▽ ̄) d
Simulation implementation of Apply
The implementation of apply is similar to call, and the code is directly given here. The code comes from zhihu @Zheng Hang’s implementation:
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if(! arr) { result = context.fn(); }else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + '] ');
}
result = eval('context.fn(' + args + ') ')}delete context.fn
return result;
}Copy the code
Next article
JavaScript in-depth simulation of bind implementation
Important reference
How to use js to implement the function of call or apply?
In-depth series
JavaScript in-depth series directory address: github.com/mqyqingfeng… .
JavaScript in-depth series is expected to write about 15, aimed at helping you understand the underlying knowledge of JavaScript, focusing on such as prototypes, scope, execution context, variable objects, this, closures, passing by value, call, apply, bind, new, inheritance and other difficult concepts.
If there are any mistakes or irregularities, please be sure to correct them. Thank you very much. If you like it or have some inspiration, welcome star, which is also a kind of encouragement to the author.