! KissyUI is a group for the front-end project Kissy on Taobao. After watching my "Reading Bad Code Series" on the company intranet, Long Zang asked in the group: How is bad code defined? ?
Yeah, what exactly is bad code? This reminds me of one thing, which is a question another netizen asked me on gtalk: He needs the three conditions a, b, and c to be false when they are all true, and false when they are all false. How to judge.
Next, students in the KissyUI group gave many answers:
[javascript] view plaincopy
en... Indeed, I have not fully verified the validity of the comprehensive answer above. Because as Long Zang later emphasized: "It seems that we are going to discuss what is bad code?" Indeed, how can we write bad code? Various strange codes appear above, including the trick used by the original questioner:
[javascript] view plaincopy
Because of this question Appearing in js, there is a weak type problem, that is, a, b, c may be integers, or strings, etc., so the (a b c)%3 approach will not work, so there is
[javascript] view plaincopy
- What if there are not three conditions a, b, and c, but two or more conditions?
- What if it is emphasized that a, b, c themselves are not necessarily Boolean values?
[c-sharp]
view plaincopy
// v0, find xor for any number of operands function e_xor() { ... }
For this e_xor( ), the most direct way to write code is:
Next, let’s consider a question. Since arguments is an array, can we use the array method? In fact, it is said that in some js environments, direct access to arguments[x] is inefficient. Therefore, the above v1 version can have a modified version:
[javascript] view plaincopy
This small code involves the use of splice/slice. Because it operates on arguments, splice may cause "singular" changes in function entry, and the performance effects in different engines are inconsistent, while slice may cause twice as much data copying. The reason why slice() is still used here is: after all, this is just a function parameter, not a "very large" array, so there is no need to overly consider storage issues.
Next, since what we get in args is an array, then It's really not that modern to use a for loop again. The correct, popular style, and not despised by the front end is:
[javascript] view plaincopy
In order to explain to some people who don’t know much about js1.6 Students with new features explain the v2 version. The following code breaks down the above implementation:
[javascript] view plaincopy
some() This method will pass each element in the array args as parameter b to callback function. some() has a feature that is consistent with our original requirements:
- when callback() returns true, some() will interrupt the enumeration of args and return the true value ; Otherwise,
- when all elements are enumerated and callback() does not return true, some() returns a false value.
Now read the v2 version of e_xor(), is it clear?
Of course, just for the sake of reducing !a operation, the v2 version can also have the following revision:
[ javascript] view plaincopy
In this line of code, continuous operations are used:
[javascript] view plaincopy
The continuous operation returns the value of the last subexpression, which is the array after slice(). This way of writing is mainly to control the code to "one expression".
Okay, now we start writing the v3 version. Why? Because the v2 version is still not cool enough, the v2 version uses Array.some(). This feature expanded in js1.6 is not so "functional" and has some traces of object-oriented. As a diehard fan of functional languages, I believe that the most normal solution to problems like "enumerating an array" is: recursion.
Why? Because a purely functional language like Erlang will not come up with the idea of Array.some()? Of course there is such a method, but from a "purer" perspective, we have to write one ourselves. hehe. How to do this kind of "pure recursion" in js? The approximate prototype will look like this:
[javascript] view plaincopy
In this framework, we set e_xor() has countless parameters, but each time we only process two a and b. If a and b are equal, we will recursively compare any one of them with the subsequent n-2 parameters. In order to achieve "recursive processing of the subsequent n-2 parameters", we need to borrow an important concept in functional languages: continuity/continuation (continuous). Dongdong Yueying once had a special topic to talk about it, here it is:
http://bbs.51js.com/viewthread.php?tid=85325
Simple In other words, continuation is to perform continuous callbacks on function parameters. This stuff is supported in the newer functional language paradigm. For this example in this article, I wrote a separate version to analyze it. I call it the tail() method, which means specifying the tail of the function parameters. It is designed as a prototype method on the function Function.
[javascript] view plaincopy
Note the interesting thing about this tail() method: it uses this.length. Functions in JavaScript have two length values. One is foo.length, which indicates the number of formal parameters of the foo function when it is declared; the other is arguments.length, which indicates the actual parameters passed in when the function is called. number. That is to say, for the function foo():
[javascript] view plaincopy
The first call will display [1,2], the second time it will Display [3,2]. In any case, the parameters a and b when declared are always two, so foo.length == arguments.callee.length == 2.
Return to the tail() method. It means:
[javascript] view plaincopy
So how is tail() used in this example?
[javascript] view plaincopy
Arguments.callee.length is used again here to determine the number of formal parameters. In other words, the end condition of the recursion is: there are only two parameters a and b left, and there is no need to scan the tail() part. Of course, the right half of the ternary expression (?:) in return will also terminate the recursion. In this case, a different condition has been found.
In this example, we write e_xor() as a tail recursive function. This tail recursion is the essence of functional style. Unfortunately, its optimization is not supported in js. . WUWU~~ I will check the resources later to see if the new chrome v8 supports it. Classmate v8, do you still want to be V5? :)
From the previous section, we saw Guy’s idea of solving problems. But at this level, the first step of abstraction is often the most critical. Simply put, V3 considers:
[javascript] view plaincopy
This framework abstraction itself may be question. The correct understanding is not "a, b are exclusive-or", but "a is exclusive-or with other elements". Therefore, the framework abstraction of v4 is:
[javascript] view plaincopy
In v3, since the b value needs to be passed to the subsequent part each time, we need to do array splicing concat() in tail(). However, when we use v4's framework, the b value itself is implicit in the subsequent part, so there is no need for splicing. In this way, tail() has a new way of writing? In fact, this is more in line with the original intention of tail(). If there is a splicing process, it should be handled by foo() instead of tail() ) to handle.
[javascript] view plaincopy
The code writing in v4 will become simpler:
[javascript] view plaincopy
- the real processing logic is only the second line.
Since everything else is part of the framework, we can consider a programming paradigm, which is an extension of tail. The purpose is to call e_xor on tail just like calling the sort() method on an array. The meaning of tail is to get data, and the meaning of the new extension is that both array and logic are taken as a whole. For example:
[javascript] view plaincopy
The usage of tailed() is very simple:
[javascript] view plaincopy
Simply speaking, we can use the xor function as the operand of tailed(). In this way, we can expose a function named tailed The core of the public library is to expose a set of functions similar to xor. Developers can use the following programming paradigm to implement operations. For example:
[javascript] view plaincopy
So, how to use this so-called tailed library? Very simple, one line of code:
[javascript] view plaincopy
Now we have a semi-mature open library called tailed. The so-called semi-mature is because our tailed() still has a small flaw, the following line of code:
[javascript] view plaincopy
The "1" in f.length 1 in the middle is a conditional parameter, which is related to the way xor processes data. To put it simply, it is precisely because a and arguments[1] need to be compared, so 1 is required here. If an algorithm needs to compare multiple operands, tailed() is not universal. So correct and complete tailed should allow the caller to specify termination conditions. For example:
[javascript] view plaincopy
xor.tailed()(a,b,c,d,e,f,g);
// or
xor.tailed(less_one)( a,b,c,d,e,f,g);
[javascript]
view plaincopy
/* tiny tailed library with templet framework, v0.0.0.1 beta. by aimingoo. */
Function.prototype.templeted = function(args) {
var buff = ['[', ,'][0]'];
buff[1] = this.toString().replace(/_([^_]*)_/g, function ($0,$1) { return args[$1]||'_'});Aren’t we looking for a piece of “less bad code”? If so, then the best way to judge the three operating conditions a, b, c is probably:
[javascript] view plaincopy
Or, if you consider Type issues to a, b, c:
[javascript] view plaincopy
If we consider the situation of judging a group of operands, then It is treated as an array and written as:
[javascript] view plaincopy
For this code, we use the JS default The access rules for arguments should be optimized if they are optimized, and forget them if they are not, because our application environment does not require such requirements as "there are thousands of arguments here" or "e_xor() is called extremely frequently". If there is no demand, the optimization we have done in this area will be a wasted function. Apart from technical perfection, it is meaningless to the application environment.
That’s enough. What we have learned is enough in the application environment, don't let the techniques spread in your code. The so-called technology is the ability to control the complexity of code and make the code beautiful, rather than making the technology itself powerful or perfect.
So, when I discussed in the "Reading Bad Code" system before, I actually emphasized three processes:
- Think clearly about the business needs first,
- Design a clear and clear calling interface,
- Implement it with the simplest and shortest distance code.
Other magic horses are just floating clouds.
=====
Note: This article from Section 2 to Section 6 is only for students who are interested in architecture, frameworks, libraries, etc. Those who are interested in studying and researching and interested in using related technologies in language design, architectural abstraction, etc., or in basic projects are welcome to discuss it, and please do not abuse it in general application projects.