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;
}
is to simulate the implementation of call. Why do you need to push a string and then use eval next? Why not directly pass in arguments[i] and then use context.fn(args)?
给我你的怀抱2017-06-12 09:32:07
Here I believe you have already understood the principle of call
. Here I will briefly explain the principle. I also refer to the instructions of the JavaScript
authoritative guide and then implement it with code.
First, let’s take a look at the syntax and definition of call
, from the Chinese version of the ECMAScript specification:
Let’s give a simple example:
var jawil = {
name: "jawil",
sayHello: function (age) {
console.log("hello, i am ", this.name + " " + age + " years old");
}
};
var lulin = {
name: "lulin",
};
jawil.sayHello(24);
// hello, i am jawil 24 years old
Then look at the output after using call
:
jawil.sayHello.call(lulin, 24);// hello, i am lulin 24 years old
Let me answer your questions based on chestnuts:
is to simulate the implementation of
call
. Why do you need to push a string and useeva
l next? Pass inarguments[i]
directly, and then usecontext.fn(args)
Why not?
First of all, you need to understand the relationship between the functions simulated above and the variables in the chestnut:
context => lulin
context.fn => jawil.sayHello
Notice this step, we just gave the reference address of jawil.sayHello
to lulin.sayHello
Original jawil.sayHello.call(context,arg1,arg2,arg3,arg4)
Eliminate context
your way to get args=[arg1,arg2,arg3,arg4]
Then execute lulin.sayHello([arg1,arg2,arg3,arg4])
Haha, it’s very confusing, isn’t it? It looks fine, but in fact it has changed. It turns out that there are four parameters, and now they are gathered together An array is an array parameter, and here's the problem.
So how to solve this problem? The idea is as above, put all the parameters into strings, and then use eval
to execute.
The effect we want is lulin.sayHello(arg1, arg2, arg3, arg4)
like this, because lulin.sayHello
needs to reorganize the parameters, you can’t get a parameter and execute the function once , or Let’s save the parameters together and execute them once. The only way I can think of is to spell all the parameters into strings, and then use eval to execute them,
lulin.sayHello([arg1,arg2,arg3,arg4]), nor
lulin.sayHello(arg1),
lulin.sayHello(arg2)...
Let’s take a brief look at the
eval function
definition and usage
The
eval() function can calculate a string and execute the JavaScript code in it.Syntax:
eval(string)
string required. A string to evaluate that contains a JavaScript expression to evaluate or a statement to execute. This method only accepts a raw string as parameter, if the string parameter is not a raw string, then the method will return unchanged. Therefore please do not pass String objects as arguments to the eval() function.
To put it simply, it uses JavaScript's parsing engine to parse the contents of this bunch of strings. Let's put it this way, you can understand it this way, you regard eval
as a <script>
tag.
eval('function Test(a,b,c,d){console.log(a,b,c,d)};
Test(1,2,3,4)')
It’s equivalent to this
<script>
function Test(a,b,c,d){
console.log(a,b,c,d)
};
Test(1,2,3,4)
</script>
Okay, let’s look at the above code again. In fact, there are still pitfalls. Let’s take a look at the modal intuitive point of view. Below is the complete debugging code:
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for (var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
console.log(args)
var star = 'context.fn(' + args + ')'
console.log(star)
eval('context.fn(' + args + ')');
delete context.fn;
}
var jawil = {
name: "jawil",
sayHello: function(age) {
console.log("hello, i am ", this.name + " " + age + " years old");
}
};
var lulin = {
name: "lulin",
};
jawil.sayHello.call2(lulin, 24, 25);
Look at the output of args:
["arguments[1]", "arguments[2]"]
Then look at the output of 'context.fn(' + args + ')':
"context.fn(arguments[1],arguments[2])"Isn’t it a bit confusing
In fact, implicit conversion is involved here. Here is an example:
What is the value of'jawil'+[1,2]+[3,4]+3
?
is equal to "jawil1,23,43"
In fact, this is equivalent to 'jawil'+[1,2].toString()+[3,4].toString()+3
The space is limited, for more implicit conversions, please refer to my article: From ++[[]][+[]]+[+[]]==10? An in-depth explanation of implicit conversions in weakly typed JS
Speaking of this, I have said all the core things. You can understand it yourself. The original author probably consulted others when writing this. Many things were not explained clearly. I guess due to the limited space, I just mentioned it in one stroke. It seems to be a very short paragraph. The code actually contains a lot of knowledge points.
曾经蜡笔没有小新2017-06-12 09:32:07
args is an array, context.fn(args)
has only one parameter. Under normal circumstances, you can use apply to convert the array into parameters, but here to simulate call, using apply is meaningless. So it uses the array's toString() to move parameters other than context to context.fn.
淡淡烟草味2017-06-12 09:32:07
Because arguments[0] is context
Didn’t you see that the loop variable starts from 1?