Recently, someone in the group posted the following question:
Implement a function, and the operation result can meet the following expected results:
add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15
For a curious Chetuzi, I couldn't help but try it out. The first thing I thought of when I saw the question was that I would use higher-order functions and()
Higher-order function: A higher-order function means that it receives another function as a parameter. In javascript, functions are first-class citizens, allowing functions to be passed as parameters or return values.
I got the following solution:
function add() { var args = (arguments); return function() { var arg2 = (arguments); return (arg2).reduce(function(a, b){ return a + b; }); } }
After verifying it, I found that it was wrong:
add(1)(2) // 3 add(1, 2)(3) // 6 add(1)(2)(3) // Uncaught TypeError: add(...)(...) is not a function(…)
The above solution is onlyadd()() It is correct in the case. However, when there are more than two or less parameters of chain operations, the result cannot be returned.
This is also a difficult point in this question.add()When, how can I return both a value and a function for subsequent calls?
Later, after expert guidance, the function was rewritevalueOfMethod ortoString Method, one of the solutions can be obtained:
function add () { var args = (arguments); var fn = function () { var arg_fn = (arguments); return (null, (arg_fn)); } = function () { return (function(a, b) { return a + b; }) } return fn; }
Um? When I first saw this solution, I was confused. Because I feel () No call has been called from beginning to end, but the following result is verified:
add(1) // 1 add(1,2)(3) //6 add(1)(2)(3)(4)(5) // 15
It's magical! Then the mystery must be on it = function() {} Inside. Why is this the case? At what time is this method executed in the function? Let me take a step by step.
valueOf and toString
Let’s briefly understand these two methods:
()
useMDN In other words, the valueOf() method returns the original value of the specified object.
JavaScript calls the valueOf() method to convert an object into a primitive type value (numeric, string, and boolean). However, we rarely need to call this function ourselves, and the valueOf method is usually automatically called by JavaScript.
Remember the above sentence, and below we will explain in detail what the so-called automatic call means.
()
The toString() method returns a string representing the object.
Each object has a toString() method which is automatically called when the object is represented as a text value or when the object is referenced in the desired string.
Remember here first that valueOf() and toString() will be called by themselves in specific occasions.
Primitive Type
OK, let’s lay the foundation, first understand the several primitive types of javascript. Excluding Object and Symbol, there are the following primitive types:
- Number
- String
- Boolean
- Undefined
- Null
When JavaScript is compared or various operations, the objects will be converted into these types, and subsequent operations will be performed. The following is explained one by one:
String Type Conversion
When a certain operation or operation requires a string, the String conversion of Object will be triggered. For example:
var obj = {name: 'Coco'}; var str = '123' + obj; (str); // 123[object Object]
Conversion rules:
- iftoString The method exists and returns the original type, toString The result.
- iftoString The method does not exist or the returned "primitive type" is calledvalueOf Method, ifvalueOf The method exists and returns the "primitive type" data, returningvalueOf The result.
- In other cases, an error was thrown.
The above example is actually:
var obj = {name: 'Coco'}; var str = '123' + ();
in,() The value of"[object Object]"。
Assume it is an array:
var arr = [1, 2]; var str = '123' + arr; (str); // 1231,2
above + arr Actually it was called+ () 。
However, we can rewrite the toString and valueOf method of the object ourselves:
var obj = { toString: function() { ('Called'); return '111'; } } alert(obj); // Called// 111
abovealert(obj) , obj will automatically call its own() The method is converted to the original type, if we don't rewrite ittoString Method, output[object Object] , here we rewritten toString, and a primitive type string 111 is returned, so finally alert comes out 111.
The above conversion rule is written that the toString method needs to exist and return the original type. If the returned type is not a primitive type, it will continue to look for the valueOf method of the object:
Let's try to prove iftoString() The system will call when the method is unavailablevalueOf() Method, let's rewrite the object'svalueOf:
var obj = { toString: function() { ('Called'); return {}; }, valueOf: function() { ('Called') return '110'; } } alert(obj); // Called// Called// 110
From the results, we can see that when toString is not available, the system will try the valueOf method again. If the valueOf method exists, it returns the original type (String, Number, Boolean) data, and returns the result of valueOf.
So what if toString and valueOf return neither the original type? See the following example:
var obj = { toString: function() { ('Called'); return {}; }, valueOf: function() { ('Called') return {}; } } alert(obj); // Called// Called// Uncaught TypeError: Cannot convert object to primitive value
Can find that iftoString andvalueOf When none of the methods are available, the system will directly return an error.
Number Type Conversion
The above description is String Type conversion will happen many times Number Type conversion:
- Call the Number() function and force the Number type conversion
- Calling () this type of parameter requires a method of type Number
- obj == 1, when comparing
- obj + 1, when performing calculations
Similar to String type conversion, but the Number type is just the opposite, first querying its ownvalueOf Method, query yourselftoString method:
- If valueOf exists and returns the original type data, the result of valueOf is returned.
- If toString exists and returns the original type data, the result of toString is returned.
- In other cases, an error was thrown.
Follow the above steps to try:
var obj = { valueOf: function() { ('Call valueOf'); return 5; } } (obj + 1); // Call valueOf// 6 var obj = { valueOf: function() { ('Call valueOf'); return {}; }, toString: function() { ('Call toString'); return 10; } } (obj + 1); // Call valueOf// Call toString// 11 var obj = { valueOf: function() { ('Call valueOf'); return {}; }, toString: function() { ('Call toString'); return {}; } } (obj + 1); // Call valueOf// Call toString// Uncaught TypeError: Cannot convert object to primitive value
Boolean Conversion
When will the Boolean conversion be performed:
- Boolean comparison
- When judging if(obj) , while(obj) , etc.
Simply put, except for the following 6 values that are converted to false, all the others are true:
- undefined
- null
- -0
- 0 or +0
- NaN
- '' (empty string)
Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean(NaN) // false Boolean('') // false
Function Conversion
OK, finally, let’s go back to our initial question and talk about function conversion.
We define a function as follows:
function test() { var a = 1; (1); }
If we just call testInstead test() , see what happens?
You can see that we have reprinted the test function we defined. In fact, the function is called by itself.valueOfmethod:
Let's rewrite the valueOf method of the test function.
= function() { ('Call the valueOf method'); return 2; } test; // The output is as follows:// Call the valueOf method// 2
Similar to the Number conversion, if the function'svalueOf The method returns not a primitive type, and it will continue to be found.toStringmethod:
= function() { ('Call the valueOf method'); return {}; } = function() { ('Call the toString method'); return 3; } test; // The output is as follows:// Call the valueOf method// Call the toString method// 3
Solve the problem
Let’s look back at the answer to the question at the beginning of my text. It is precisely when using the function, it will call it by itself. valueOf Method this technique and rewritten the method. We changed it slightly and the deformation is as follows:
function add () { ('Enter add'); var args = (arguments); var fn = function () { var arg_fn = (arguments); ('Call fn'); return (null, (arg_fn)); } = function () { ('Call valueOf'); return (function(a, b) { return a + b; }) } return fn; }
When add is called once, it actually returns the function fn, which is actually the return();
add(1); // The output is as follows:// Enter add// Call valueOf// 1
In fact, it is equivalent to:
[1].reduce(function(a, b) { return a + b; }) // 1
When chained calls twice:
add(1)(2); // The output is as follows:// Enter add// Call fn// Enter add// Call valueOf// 3
When chained three times:
add(1)(2)(3); // The output is as follows:// Enter add// Call fn// Enter add// Call fn// Enter add// Call valueOf// 6
As you can see, there is actually a cycle here. Only the last call is actually called valueOf, while the previous operations were all merged parameters, and the recursive call itself. Since the last call returned a fn function, the function was finally called, and used reduce The method sums all parameters.
Except for rewritingvalueOf Method, can also be rewritten toString Method, so, if you like, the following is OK:
function add () { var args = (arguments); var fn = function () { var arg_fn = (arguments); return (null, (arg_fn)); } = function() { return (function(a, b) { return a + b; }) } return fn; }
There is a rule here, if only rewritevalueOf() OrtoString() One of them will give priority to calling the rewritten method, and if two are rewritten at the same time, it will give priority to query like the String conversion rule.valueOf() Method, invalueOf() The method returns a non-primitive type and then query it. toString() method.
If you can read it carefully, I believe you will gain something.
The above is all the content of this article. I hope that the content of this article will help you study or work. I also hope to support me more!