The topic of anti-corification comes from a tweet last year by Brendan Eich, the father of JavaScript. I have been researching it in the past few days and found this stuff very interesting, so I would like to share it. Forget its name first, let’s take a look at what it can do.
Don’t underestimate this function. Just imagine, when we write a library, we often write such code, take webQQ’s Jx library as an example.
What we want is actually just to borrow some functions on the Array prototype chain. There is no need to explicitly construct new functions to change their parameters and re-evaluate them.
It is obviously more elegant and beautiful to use the uncurrying method, like this:
You can also do a lot of interesting and convenient things.
You can even uncurrying both the call and apply methods, and treat the function as Use ordinary data. This makes the function calling method in JavaScript more like its predecessor scheme. When the function name itself is a variable, this calling method is particularly convenient. The function calling in the
scheme is like this:
It can be written very close in javascript.
Look at the jquery library again. Since the jquery object (i.e. the object created through $()) is a pseudo-array pretending to be an object, it has a length attribute and can be subscripted Find the corresponding element. When you need to add a member to the jquery object, the pseudo code is probably:
If you use uncurrying, you can
borrow the push function of the array object and let the engine manage it automatically Array members and length properties.
And you can borrow all the required functions at once, once and for all. A piece of test code:
In general, using uncurrying technology, any object can have the methods of a native object. Good , if it still doesn’t arouse your interest, then you can go do something else.
Let’s take a look at the principle and implementation step by step.
Before understanding the strange name of de-currying, we must first understand currying.
Definition from Wikipedia: Currying; also known as partial evaluation, is to transform a function that accepts multiple parameters into a function that accepts a single parameter, and returns a new function that accepts the remaining parameters and returns the result Technology.
In layman terms, currying is a bit like the installment payment method when buying a house. You first give a part of the down payment (part of the parameters), return a passbook (return a function), and then give the remaining parameters and evaluate the calculation when appropriate.
Let’s take a look at currying that we have all used. We often implement a Function.prototype.bind function when binding context.
Higher-order functions are the basis for implementing currying. The so-called high-order functions at least meet these requirements 2 features:
1, functions can be passed as parameters,
2, functions can be used as return values.
At the beginning of its design, Javascript referenced many features of the scheme language. Scheme is one of the two major dialects of Lisp, the originator of functional languages, so JavaScript also has some features of functional languages, including higher-order functions, closures, lambda expressions, etc.
When a function in JavaScript returns another function, a closure will be formed, and the parameters of the first operation can be saved in the closure. We use this idea to write a general currying function.
We agreed that when parameters are passed in, currying will continue, and evaluation will only start when the parameters are empty.
Suppose we are implementing a function that calculates monthly expenses. Before the end of each day, we have to record today’s expenses. How much is it, but we only care about the total cost at the end of the month, there is no need to calculate it every day.
Using the currying function, calculations can be delayed until the last minute. The benefits are self-evident. In many cases, unnecessary calculations can be avoided, saving performance, and it is also a solution to achieve lazy evaluation.
Okay, now Coming to the topic,
curring is to pre-fill some parameters.
Anti-curring is to delay the original fixed parameters or this context as parameters to be passed to the future.
In fact, it is to do such a thing, to:
1
obj.foo( arg1 ) //foo is originally a function only on obj. Just like push is originally only on Array.prototype
converted into this form
1
foo( obj, arg1 ) / / Same as the first example we gave. Convert [].push to push( [] )
Just like the socket that was originally connected to the TV plug, after removing it, it can actually be used to connect the refrigerator .
There is such a paragraph after each prototype method of Array and String on Ecma, such as push:
NOTE The push function is intentionally generic; it does not require that its this value be an Array object.
Therefore it can be transferred to other kinds of objects for use as a method. Whether the concat function can be applied.
Why is Javascript designed this way? Let’s first review the important duck typing ideas in dynamic languages.
Tell a story:
A long time ago, there was an emperor who liked to hear ducks quacking, so he summoned his ministers to form a choir of a thousand ducks. The minister caught all the ducks in the country, but in the end there was still one missing. One day, a volunteer chicken finally came. This chicken said that it could also croak. Well, in the setting of this story, it did indeed croak. Later in the story it becomes clear that the chicken got mixed up in the duck's choir. - The emperor just wants to hear croaking, he doesn't care whether you are a duck or a chicken.
This is the concept of duck typing. In JavaScript, many functions do not do object type detection, but only care about what these objects can do.
The methods on the prototype of the Array constructor and String constructor are specially designed to be duck-typed. These methods do not perform any verification on the data type of this. This is why arguments can pretend to be array and call the push method.
Look at the code of Array.prototype.push in the v8 engine:
function ArrayPush() {
var n = TO_UINT32( this.length );
var m = %_ArgumentsLength();
for (var i = 0; i
this[i+n] = %_Arguments(i); //Attribute copy
this.length = n + m; //Correction length
return this.length;
}
}
As you can see, the ArrayPush method does not impose any explicit restrictions on the type of this, so in theory any object can be passed in ArrayPush this visitor.
There is only one problem left that we need to solve, how to make an object pretend to be an array object in a general way.
The real implementation code is actually very simple:
Although this code is very short, it is still a bit hard to understand when you first understand it. Let’s take the push example to see what happens.
var push = Array .prototype.push.uncurrying();
push( obj, 'first' );