Identify text signatures
Let’s first review the Overload use cases mentioned in the previous article:
var extend = Overload
.add("*, ...",
function(target) { })
.add("Boolean, *, ...",
function(deep, target) { });
We allow the user to enter a string representing a certain overloaded signature. When the user calls a function, we need to compare the parameter instances entered by the user to each parameter type on the signature, so we need to convert this string into a type array first. That is, the string "Boolean, Number, Array" should be converted to an array [Boolean, Number, Array].
Before performing conversion, we must first consider dealing with two special types, namely "*" representing any type and "..." representing any number of "...". We can define two proprietary types for them to make special compatibility treatments for them within Overload:
= function() {};
= function() {};
After these two types, the string "Boolean, *, ..." is correctly converted to an array [Boolean, , ]. Since sum is both functions, it can naturally be regarded as types.
After these two types are correctly processed, we can start writing conversion functions that recognize text signatures:
if ((/(^\s+|\s+$)/ig, "") == "") {
signature = [];
} else {
signature = (",");
for (var i = 0; i < ; i++) {
var typeExpression = signature[i].replace(/(^\s+|\s+$)/ig, "");
var type = null;
if (typeExpression == "*") {
type = ;
} else if (typeExpression == "...") {
type = ;
} else {
type = eval("(" + typeExpression + ")");
}
signature[i] = type;
}
}
I think this code is quite easy to understand, so I won't explain it anymore. When I first wrote this code, I forgot to write the first if above, which caused the blank signature string "" to be correctly recognized as a blank signature array[]. Fortunately, my unit test code discovered this defect immediately. It seems that writing unit test code is still very important.
Match function signature
After we get the type array of function signatures, we can use it to match the instance array of input parameters to find the correct overload. Before discussing how to match function signatures, let's first look at how C# or such languages handle function overload matching. The process of function overload matching in general languages is like this:
Number of parameters - Overloads with incorrect number of parameters will be excluded
Parameter type - Parameter type cannot be implicitly converted to signature type will be excluded
Number of matches - After excluding, the number of matched signatures remaining is different.
0 matches - no hit matches
1 match - This is the match of hit
2 or more matches - If one of the best matches can be found among these matches, hit the best match; otherwise, no matches will be hit
In this section, we first deal with the first two steps in the process, and remove signatures with inconsistent numbers or parameter types:
var matchSignature = function(argumentsArray, signature) {
if ( < ) {
return false;
} else if ( > && !) {
return false;
}
for (var i = 0; i < ; i++) {
if (!(signature[i] ==
|| argumentsArray[i] instanceof signature[i]
|| argumentsArray[i].constructor == signature[i])) {
return false;
}
}
return true;
};
In order to compare the length, we need to make a special treatment for the "..." representing the number of any parameters outside this function:
if (signature[ - 1] == ) {
= - 1;
= true;
}
This piece of code will be integrated into the end of the conversion function in Section 1 so that the matchSignature function can easily determine whether the parameters and signature match. In the ideal case, we match the input parameter type to 0 or 1 overload, so we can easily tell which overload was hit. But if there are 2 or more overload matches, then we have to choose one of the best ones, which is exactly what we will discuss in the next section.
Handle multiple matches
For how C# selects more matching overloads from multiple matches, you can see the relevant chapters in C# Language Specification. I think three simple examples can be explained:
long Sum(int x, int y) { return x + y; }
long Sum(long x, long y) { return x + y; }
Sum(0, 1);
Since the two parameters 0 and 1 are understood by the compiler as int type, they do not need to be typed for the first overload, and both of them need to be typed for the second overload, so the first overload is better.
long Sum(int x, long y) { return x + y; }
long Sum(long x, int y) { return x + y; }
Sum(0, 1);
On the first parameter, the first overload is better; on the second parameter, the second overload is better. In this case, no overload is better than the other, and an error will be reported if the better overload compiler cannot be found.
long Sum(int x, int y) { return x + y; }
long Sum(int x, long y) { return x + y; }
long Sum(long x, int y) { return x + y; }
Sum(0, 1);
On the first parameter, the first overload is better than the third overload, and is no different from the second overload; on the second parameter, the first overload is better than the second overload, and is no different from the third overload. Although the second overload cannot distinguish between the third overload, we can be sure that the first overload is better than both of them, so the compiler chose the first overload.
Suppose we have an overloadComparator comparison function that can compare the advantages and disadvantages between any two signatures. Do we need to compare the signatures only in pairs to find out the optimal overload? In fact, it is not necessary. We can use Array's sort method and let it call overloadComparator for sorting. After sorting, then verify the relationship between the first two. If it is parallel, no one will be hit; if there is a sequence, the first one will be hit.
The specific overloadComparator code is not given here. It relies on another comparison function called inheritanceComparator to compare which of the two signed parameter types is more appropriate to the actual passed parameter type. It uses a more clever method to determine whether the two types are inherited and who inherits from whom.
summary
Now we have a JavaScript function overload library. Please see the complete code here: Function overload library Overload. I hope this library can effectively help everyone improve the readability of JavaScript code and reduce the maintenance costs of large Ajax projects.
Let’s first review the Overload use cases mentioned in the previous article:
Copy the codeThe code is as follows:
var extend = Overload
.add("*, ...",
function(target) { })
.add("Boolean, *, ...",
function(deep, target) { });
We allow the user to enter a string representing a certain overloaded signature. When the user calls a function, we need to compare the parameter instances entered by the user to each parameter type on the signature, so we need to convert this string into a type array first. That is, the string "Boolean, Number, Array" should be converted to an array [Boolean, Number, Array].
Before performing conversion, we must first consider dealing with two special types, namely "*" representing any type and "..." representing any number of "...". We can define two proprietary types for them to make special compatibility treatments for them within Overload:
Copy the codeThe code is as follows:
= function() {};
= function() {};
After these two types, the string "Boolean, *, ..." is correctly converted to an array [Boolean, , ]. Since sum is both functions, it can naturally be regarded as types.
After these two types are correctly processed, we can start writing conversion functions that recognize text signatures:
Copy the codeThe code is as follows:
if ((/(^\s+|\s+$)/ig, "") == "") {
signature = [];
} else {
signature = (",");
for (var i = 0; i < ; i++) {
var typeExpression = signature[i].replace(/(^\s+|\s+$)/ig, "");
var type = null;
if (typeExpression == "*") {
type = ;
} else if (typeExpression == "...") {
type = ;
} else {
type = eval("(" + typeExpression + ")");
}
signature[i] = type;
}
}
I think this code is quite easy to understand, so I won't explain it anymore. When I first wrote this code, I forgot to write the first if above, which caused the blank signature string "" to be correctly recognized as a blank signature array[]. Fortunately, my unit test code discovered this defect immediately. It seems that writing unit test code is still very important.
Match function signature
After we get the type array of function signatures, we can use it to match the instance array of input parameters to find the correct overload. Before discussing how to match function signatures, let's first look at how C# or such languages handle function overload matching. The process of function overload matching in general languages is like this:
Number of parameters - Overloads with incorrect number of parameters will be excluded
Parameter type - Parameter type cannot be implicitly converted to signature type will be excluded
Number of matches - After excluding, the number of matched signatures remaining is different.
0 matches - no hit matches
1 match - This is the match of hit
2 or more matches - If one of the best matches can be found among these matches, hit the best match; otherwise, no matches will be hit
In this section, we first deal with the first two steps in the process, and remove signatures with inconsistent numbers or parameter types:
Copy the codeThe code is as follows:
var matchSignature = function(argumentsArray, signature) {
if ( < ) {
return false;
} else if ( > && !) {
return false;
}
for (var i = 0; i < ; i++) {
if (!(signature[i] ==
|| argumentsArray[i] instanceof signature[i]
|| argumentsArray[i].constructor == signature[i])) {
return false;
}
}
return true;
};
In order to compare the length, we need to make a special treatment for the "..." representing the number of any parameters outside this function:
Copy the codeThe code is as follows:
if (signature[ - 1] == ) {
= - 1;
= true;
}
This piece of code will be integrated into the end of the conversion function in Section 1 so that the matchSignature function can easily determine whether the parameters and signature match. In the ideal case, we match the input parameter type to 0 or 1 overload, so we can easily tell which overload was hit. But if there are 2 or more overload matches, then we have to choose one of the best ones, which is exactly what we will discuss in the next section.
Handle multiple matches
For how C# selects more matching overloads from multiple matches, you can see the relevant chapters in C# Language Specification. I think three simple examples can be explained:
Copy the codeThe code is as follows:
long Sum(int x, int y) { return x + y; }
long Sum(long x, long y) { return x + y; }
Sum(0, 1);
Since the two parameters 0 and 1 are understood by the compiler as int type, they do not need to be typed for the first overload, and both of them need to be typed for the second overload, so the first overload is better.
Copy the codeThe code is as follows:
long Sum(int x, long y) { return x + y; }
long Sum(long x, int y) { return x + y; }
Sum(0, 1);
On the first parameter, the first overload is better; on the second parameter, the second overload is better. In this case, no overload is better than the other, and an error will be reported if the better overload compiler cannot be found.
Copy the codeThe code is as follows:
long Sum(int x, int y) { return x + y; }
long Sum(int x, long y) { return x + y; }
long Sum(long x, int y) { return x + y; }
Sum(0, 1);
On the first parameter, the first overload is better than the third overload, and is no different from the second overload; on the second parameter, the first overload is better than the second overload, and is no different from the third overload. Although the second overload cannot distinguish between the third overload, we can be sure that the first overload is better than both of them, so the compiler chose the first overload.
Suppose we have an overloadComparator comparison function that can compare the advantages and disadvantages between any two signatures. Do we need to compare the signatures only in pairs to find out the optimal overload? In fact, it is not necessary. We can use Array's sort method and let it call overloadComparator for sorting. After sorting, then verify the relationship between the first two. If it is parallel, no one will be hit; if there is a sequence, the first one will be hit.
The specific overloadComparator code is not given here. It relies on another comparison function called inheritanceComparator to compare which of the two signed parameter types is more appropriate to the actual passed parameter type. It uses a more clever method to determine whether the two types are inherited and who inherits from whom.
summary
Now we have a JavaScript function overload library. Please see the complete code here: Function overload library Overload. I hope this library can effectively help everyone improve the readability of JavaScript code and reduce the maintenance costs of large Ajax projects.