The following are two implementations of uncurring
Implementation 1
Function.prototype.uncurrying = function(){
var self = this;
return function(){
// 获取传入的上下文对象
var context = Array.prototype.shift.call(arguments);
// 这里的this是调用uncurrying者
return self.apply(context, arguments);
};
};
var push = Array.prototype.push.uncurrying ();
var arr = [];
push(arr, 1); // ==> arr = [1]
push(arr, 4); // ==> arr = [1, 4]
Implementation 2
Function.prototype.uncurrying = function(){
var self = this;
return function(){
return Function.prototype.call.apply(self, arguments);
};
};
var push = Array.prototype.push.uncurrying ();
var arr = [];
push(arr, 1); // ==> arr = [1]
push(arr, 4); // ==> arr = [1, 4]
The two results are the same, but I am a little confused about the second implementation method, mainly here
第一种方式显示的用self,在这里也就是push方法执行了一下,
self.apply(context, arguments);
但是如下第二种实现方式,却没有发现self执行的痕迹,
按我的理解这里就是用apply修改call的上下文为self,这里也就是push,
但这样有执行push方法吗?难道call内部的实现帮忙执行了self?求解
Function.prototype.call.apply(self, arguments);
I was instantly enlightened by you, thank you!
louiszhai
Function.prototype.call.apply(self, arguments);
先用apply修改了call的上下文为self,
后续调用uncurrying,相当于在self上调用call方法,也就执行了self
Function.prototype.call.apply(self, arguments);
This may seem a bit convoluted, but it’s actually easy to understand.In fact, your second implementation can also lead to the third implementation of anti-currying:
Next, I will analyze your second implementation first, and then analyze your third implementation. Your implementation looks like this:
Whoever calls
Theuncurrying
will be equal tothis
orself
. This means thatself
is thearray push method
.uncurrying
,谁就等于this
或self
. 这意味着self
就是数组的push方法
.替换掉
self
,最终外部的push
replacesself
, and the final externalpush
is equivalent to the following function:function is placed here. Let’s first understand that
apply
函数,apply
has the function of decomposing the array into parameters.Derivation formula:
a.apply(b, arguments)
意味着把b当做this上下文,相当于是在b上调用a方法,并且传入所有的参数,如果b中本身就含有a方法,那么就相当于b.a(arg1, arg2,…)
Formula 1:
a.apply(b, arguments) === b.a(arg1, arg2,…)
Since
call
和apply
except for inconsistent parameter processing, other functions are consistent, then the formula can be further evolved to get:Formula 2:
a.call(b, arg) === b.a(arg)
Substitute Formula 1 into the above function, we get:
a = Function.prototype.call
That is, a is equal to the call method.We then plug in the formula, we have:
b = Array.prototype.push
That is, b is equal to the push method of the arrayThen
Function.prototype.call.apply(Array.prototype.push, arguments)
is relative to:Array.prototype.push.call(arg1, arg2,…)
, then:push([], 1)
就相当于Array.prototype.push.call([], 1)
, and then substitute it into Formula 2, which is equivalent to:[].push(1)
The answer is already obvious, which is to add the number 1 to the end of the array.
Next, let me analyze the third implementation of anti-currying:
For
this.call.bind(this);
部分,this
相当于Array.prototype.push
, then the whole is equivalent to the following:Array.prototype.push.call.bind(Array.prototype.push)
The difficulty here lies in the bind method. The implementation of bind is relatively simple, as follows:
If you want to understand, you must simplify the complex. The simpler you understand, the more thorough you will understand. To further simplify
bind
的原理,等同于谁调用bind
, just return a new function.We assume that the function
fn
调用bind
方法如fn.bind([1, 2])
,经过简化,忽略bind
binds the parameters and finally returns the following:The above will be
fn
替换为Array.prototype.push.call
,[1, 2]
替换为Array.prototype.push
, then:Array.prototype.push.call.bind(Array.prototype.push)
will be equivalent to:This looks a little different from the second implementation of anti-currying. Don’t worry, although it looks inconsistent on the surface, it is still consistent at the core. Please be patient and read below:
The difference lies in the first half
Array.prototype.push.call
,这里它是一个整体,实际上想代表的就是call方法。而我们都知道,所有函数的call方法,最终都是Function.prototype
的call
method. Then, the following identity holds:Then the above function will be equivalent to:
Removing the substituted parameters, the function can be restored to:
To sum up, the final third implementation of anti-currying will be completely consistent with the second implementation. The reasoning is complete and the coding is not easy. If you like it, please give it a like and thank you~
In order to deepen my understanding of
bind
and currying, I also wrote a blog to analyze them in depth.Please refer to Currying and Decurrying in Functional Programming and Function.prototype.bind Method Guide.
Students who like it can also follow my column Lewis’s front-end in-depth course
Basics
The differences and functions of call and apply will not be described in detail
Call and apply source code implementation
They are very close. Here we only introduce call, for example: a.call(b, c)
Take out the first parameter x = b || {}
x.fn = a
Splice the parameters except the first parameter, separated by commas, the result is d
Create a function e = new Function() in an independent execution environment, and execute x.fn(d) inside the function
Execute the created e
Understanding of Solution 2
The issue of call and apply to expand the object method is not considered here, because the methods will be dynamically created from the source code, so this issue will not be discussed in detail below.
self points to Array.prototype.push
(Function.prototype.call).apply(Array.prototype.push, arguments);
Use the source code just explained to transform 2 and get: Array.prototype.push.(Function.prototype.call)(arguments). It also needs to be converted here. call accepts not an array, see 4.
arguments is an array-like object [arr, 1]. Transform 3 to get: Array.prototype.push.(Function.prototype.call)(arr, 1)
The source code of call has been explained, so change 4, and get arr.(Array.prototype.push)(1)
Write it better, arr.push(1)