“Programs are written for people to read, and only occasionally for computers to execute.” — Donald Ervin Knuth, Gartner
About Code Literacy
We often talk about the word “literacy”, refers to the individual practice training in the professional field and become a kind of culture, in different areas have different reflected, such as in the field of music, “music accomplishment” refers to the individual to the sense of music, the rhythm of pitch of the accused, in different genres of music appreciation ability, etc., and in the field of programming, also have different quality, It reflects basic skills, code cleanliness, professional attitude and so on. “Code literacy” simply means whether the code is elegant, beautiful and maintainable.
There is no such thing as absolutely perfect code, and code literacy does not mean perfectionism. In the field of translation, there is a standard of “faithfulness, expressiveness and elegance”. The reason why “elegance” is put last is that to achieve it, one needs to have a relatively high standard and experience accumulation. When we think about programming, the first thing we think about is how to implement business logic, not how to write code elegantly, so there is no such thing as absolute elegance in writing code. However, as a professional front-end engineer, precisely, should be a professional software engineer, elegant code should be keep pursuit, it’s more like a plumb line, as everyone knows what to do, what not to do, the so-called principle, the so-called bottom line, the so-called “code quality”.
Broken window theory
Broken window theory, the original meaning is that the Windows are broken, the building is neglected, people let the Windows continue to be broken, and eventually they also participate in the destruction activities, graffiti on the outside wall, garbage accumulation, and finally fall down.
The broken window theory is very easy to appear in practice. Often, the first person’s code is not good, and the second person will have the thought of “anyway, he has already written it like this, so I can only do this”. As a result, the maintenance of the code becomes more and more complicated, and the code collapses at the last moment, turning into garbage that no one wants to maintain.
Clean code
Clean code is like beautiful prose. Think of a good book you’ve read, full of pictures and emotions that follow the author’s strokes. Code may not be as climactic, but clean code should be filled with tension that can be harnessed at some point to bring the plot to a climax.
I prefer to write code to write a story, writing code is a process of creation, the author needs to be what they want to express through the form of a code, code and tidy like telling a story, drawing, fascinating, bad code, let a person feel no idea throughout don’t know what’s talking about.
Clean code principles
In modern front-end development, there are a lot of automated tools to help us write standard code, such as ESLint, TSLint and other auxiliary verification tools, well-known specifications such as Google specification, Airbnb specification and so on from every detail constraints, to help us form a reasonable standard code style.
This section does not repeat the code style at the language level. Based on the actual refactoring project, it summarizes a series of principles that need to be paid attention to during the development process, in order of importance.
1. DRY(Don’t Repeat Yourself)
As a software engineer, you’ve all heard of the basic DRY principle, and many design patterns, including object orientation itself, work on it.
DRY, as the name implies, is refers to the “don’t repeat yourself”, it emphasizes the principle of an abstract, if the same or similar code snippet appeared in two or more, so it should be abstracted into a general method or file, where you need to use to depend on the introduction, to ensure that at the time of change, only need to adjust a, all the local change to come over, Instead of going to every place to find the appropriate code to change.
In practice, I’ve seen two examples of code that take this principle to extremes:
- One is no abstraction at all, with duplicate code scattered all over the place, or even weirder, with some abstraction but more repetition, such as one extracted under Common
data.js
Data processing file, some pages reference the file, and many pages copy the file completely several different method code. The author’s intent is ironic — only a small amount of code, not the entire file. Although the modern front-end build layer can solve this problem, even with the introduction of the entire large file, this part of the excess code does not suffer much performance loss after Gzip, but this behavior of copying around doubles the subsequent maintenance costs. - For this behavior also encountered another reason, is short period of time, change the previous code, afraid of causing problems on the Internet, then copy a copy of the same logic to modify. Such as payment of logic, the logic of the original into individual UI floating layer + individual purchases, now put forward the “package” demand, the original code logic and more complex, appeared the phenomenon of “change the fixed”, so the UI layer and the logical several files the entire copy of the purchase, change a few times, formed a new “packaging” module, Later the product put forward “buy by article”, according to the above “change not to move” principle, and a copy of “buy by article” module. So call the logic will be redundant repetition, need according to the different way of buying into different UI components and logic, and if the new requirements, such as “payment by installments” support, so will change a lot of documents, the most sadly, finally want to refactor the code into a unified call, will face three pressure “to change the fixed”, It requires comparative analysis to extract similarities among many logics, and the workload can no longer be measured by doubling, which is often not recognized and understood by the product.
- Another extreme is excessive design, write each logical time to abstract, let can dramatically reduce the readability of the code, a simple for loop to reuse, variable definitions, even to maintain this code is also more cost, and will be a very different logic too abstract, makes abstract method is very complex, often “single point”, This behavior is also undesirable.
This is why this principle is ranked in the first place. This behavior leads to the largest amount of refactoring work. Maintaining good code maintenance is a kind of accomplishment, but also a kind of responsibility.
2. SRP(Single Responsibility Principle)
SRP is also a well-known design principle — single responsibility. In object-oriented programming, classes should have a single responsibility, and a class should have only one motivation to change.
For front-end development, the need to implement the thought is the function should remain single responsibility, and a function should do only one thing at a time, and so is the guarantee function of reusability, a single function has better reusability, and can make the overall framework of code clearer, details are encapsulated in the small functions. The other thing that has to do with the single responsibility, is that functions that have no side effects, also called pure functions, we should try to keep the number of pure functions as high as possible, non-pure functions are inevitable, but we should try to minimize them.
The SRP principles in second place, because it is very important, no one wants to see the logic of a mess, in the maintenance of code, without a clear logical structure, all the data definition, data processing, DOM manipulation, and so on a series of all the details of the code in a function, causing the function is very long, let a person produce psychological instinctively rejected, Unwilling to look at internal logic.
All of the complex logic in a single function will raise eyebrows when you see code like this:
show: function(a, b) {
if(! isInit) { init(); isInit =true;
}
// reset
this.balance = 0;
this.isAllBalance = false;
var shouldShowLayer = true,
preSelectedTermId = 0,
needAddress = course.address_state,
showTerms,
termsObj;
var hasPunish = false; this.course = course = course || {}; opt = opt || {}; opt.showMax = opt.showMax || 6; (isIosApp || b.isIAP) && (usekedian = ! 0, priceSymbol ='<i class="icon-font i-kedian"></i>'),
f.splice(b.showMax), layer.show({
$container:b.$container,
content:termSelectorTpl({
terms:f,
curTermId:b.curTermId || d,
name:a.name,
hasPunish:h,
userInfo:j
}, {
renderTime:T.render.time.renderCourseTime,
renderCourseTime:renderCourseTime,
hideUserInfo:b.hideUserInfo,
hideTitle:b.hideTitle,
hidePayPrice:b.hidePayPrice,
confirmText:b.confirmText,
sys_time:a.sys_time
}),
cls:"term-select-new",
allowMove:function(a) {
return opt.allowMove || ($target.closest('.select-content').length &&
$('.term-select-new .select-time').height() +
$('.term-select-new .select-address').height() +
$('.term-select-new .select-discounts').height() > (winWidth > 360 ? 190 : winWidth > 320 ? 175:150)); }, afterInit:function(c) {
if(needAddress) { that.loadAddress(); // If the address is required, and it is an app, the address needs to be updated during the screen visibility switchif (isApp) {
$(document).on(visibilityChange, function (e) {
// console.log('visibilityChange',document[hidden]);
if(! document[hidden]) { //trueThe parameter indicates that that. LoadAddress (true); }}); } } that.afterTermSelect();$dom.on('click'.'.layer-close'.function() {
setTimeout(function() {
!opt.noAutoHide && layer.hide();
}, 100);
opt.onCancel && opt.onCancel();
});
$dom.on('click'.'.term'.function(e) {
var $this = $(this);
var $terms = $('.term');
if (!$this.hasClass('disabled')) {
$terms.removeClass('selected');
$this.addClass('selected');
}
that.afterTermSelect();
});
$dom.on('click'.'.layer-comfirm'.function(e) {
var $this = $(this);
var termId = $dom.find('.term.selected').data('term-id');
var termName = $dom.find('.term.selected').find('.term-title').html();
var discountId = $dom.find('.discounts-list_item.selected').data('discount-id');
var couId = $dom.find('.discounts-list_item.selected .discounts-coupon').data('cou-id');
var directPay = false; // ios hand Q IAPif(thate.torecharge) {var toRechargePrice = thate.curprice-thate.balance;if (isIosApp) {
require.async('api'.function (api) {
api.invoke('api'.'balanceRecharge', { amount: toRechargePrice }); // Setup callback API. AddEventListener ('balanceRechargeCallBack'.function(data) {// if the payment is successful // code=0, it is successful; otherwise, it is failed. Var directPay = data.code === 0 && data.mode === 2; ToGetBalance (that.course, termId,function() {
directPay && $this.trigger('click');
});
});
});
} else {
var toRechargePrice = that.curPrice - that.balance;
if (that.rechargeMap &&
Object.keys(that.rechargeMap).indexOf("" + toRechargePrice) > -1) {
that.opt.onComfirmClick && that.opt.onComfirmClick(1);
iosPay.iosRecharge({
productId: that.rechargeMap[toRechargePrice],
count: toRechargePrice,
succ: function() {
that.toGetBalance(that.course, $('.term.selected').data('term-id')); }}); }else {
that.opt.onComfirmClick && that.opt.onComfirmClick(2);
// T.jump('/iosRecharge.html? _bid=167&_wv=2147483651');
that.jumpPage('/iosRecharge.html? _bid=167&_wv=2147483651'); }}return;
}
if(! termId) { require.async(['modules/tip/tip'].function(Tip) {
Tip.show(opt.dialogTitle);
});
return true;
}
// check address
if(needAddress && ! that.addressid) {if (course.must_fill_mailing || !$dom.find('.select-address').hasClass('z-no') {// If the address is not filled in, the address box should be marked red, and then you need to slide to the window to let the user see the var$cnt = $dom.find('.select-content');
var $addressWrap = $dom.find('.select-address_wrapper').addClass('z-err');
var cntRect = $cnt[0].getBoundingClientRect();
var addressBoxRect = $addressWrap[0].getBoundingClientRect();
// console.log('> > > > >', cntRect, addressBoxRect);
if (addressBoxRect.bottom > cntRect.bottom) {
$cnt.scrollTop($cnt.scrollTop() + (addressBoxRect.bottom - cntRect.bottom));
}
return; }}if (that.isAllBalance && that.opt.onComfirmClick) {
that.opt.onComfirmClick(3);
}
opt.cb && opt.cb(termId, discountId, couId, termName, that.isAllBalance, that.payBalance, that.addressid);
setTimeout(function() {
!opt.noAutoHide && layer.hide();
}, 300);
});
$dom.on('click'.'.discounts-list_item'.function(e) {
var $this = $(this);
var $discounts = $('.discounts-list_item');
var isSelected = $this.hasClass('selected');
if (!$this.hasClass('disabled')) {
$discounts.removeClass('selected');
$this.addClass(isSelected ? ' ' : 'selected'); that.setPayPrice(); }});$dom.on('click'.'.address-person .i-edit2, .address-add'.function() {
var termId = $dom.find('.term.selected').data('term-id');
var courseId = that.course.cid;
var src = '/addrEdit.html? _bid=167&_wv=2147483649&ns=1&fr=' + (location.pathname.indexOf('allCourse.html') > 1? 4 : location.pathname.indexOf('courseDetail.html') > 1? 2, 3) +'&course_id=' + courseId + '&term_id=' + termId;
// T.jump(src);
that.jumpPage(src);
}).on('click'.'.select-address_title .i-right-light'.function(e) {
var $addressDom = $dom.find('.select-address');
var isOpen = !$addressDom.hasClass('z-no');
if (isOpen) {
$addressDom.addClass('z-no');
that.theAddressid = that.addressid;
that.addressid = undefined;
} else {
$addressDom.removeClass('z-no'); that.addressid = that.theAddressid; }}); }}); }else{ opt.cb && opt.cb(opt.curTermId || preSelectedTermId); }}Copy the code
A single responsibility does not have to be accomplished by many functions. It can also be accomplished by sections, as in this case:
show(data) {
data && this.setData(data);
const renderData = {
data: this.data,
courseData: this.data.courseData,
termList: this.termList,
userInfo: this.userInfo,
addrList: this.addrList,
isIAP: this.isIAP,
balance: betterDisplayNum(this.balance),
curPrice: betterDisplayNum(this.curPrice),
curTermId: this.curTermId,
discountList: this.discountList,
curDisId: this.curDisId,
jdSelectId: this.jdSelectId,
curAddrId: this.curAddrId
};
const formatters = {
// formatters
termFormatter,
priceFormatter,
okBtnFormatter,
balanceFormatter,
priceFormatterWithDiscount
};
console.log('[render data]: ', renderData); const html = payLayerTpl(renderData, formatters); This._setscrolltop (); // Prevent duplicate appendsif (this.$view) {
this.$view.replaceWith(html);
} else {
this.$container.append(html);
}
afterUIRender(() => {
this.$view = $('. '+ COMPONENT_NAME).show(); this._setContentHeight(); This._restorescrolltop (); // Dynamically set the height of the scroll area this._restorescrollTop (); // Restore the scroll position this._initevent (); this._initCountDown(); }); }Copy the code
Although this function does not maintain a single responsibility, the internal flow logic is clearly expressed in a “segmented” form, and the code looks much better than if all the details were put together in one function.
For single responsibility, still more difficult to keep up, is mainly for the separation of duty, sometimes too carefully the responsibility of the split will bring bigger difficult to read, in this case, still take writing to contrast, a single responsibility is equivalent to the article “paragraph”, for the article, the central idea of each paragraph has it, can be described in one sentence, If you find that the central idea of a function is vague, or that it requires a lot of language to describe it, then it probably has many responsibilities that should be split.
3. LKP(Least Knowledge Principle)
The LKP principle is the principle of least knowledge, also known as Demeter’s law, which says that one object should know the least about the other, it doesn’t matter how complex you are internally, I only care where you call.
Keeping the profile of exposed interfaces easy to use is also a general rule of API design, and a UI component like this has been found in practice:
module.exports = {
show: function(course, opt) {// omit a bunch of logic here}, jumpPage:function(url) {// omit a bunch of logic here}, afterTermSelect:function() {// omit a bunch of logic},setPrice: function() {// omit a bunch of logic},setBalance: function() {// omit a bunch of logic here}, toGetBalance:function(course, curTermId, cb) {// omit a bunch of logic},setDiscounts: function(course, curTermId, curPrice) {//functionIf the discounts are too high, they are isSuitCoupon.function(cou, curPrice) {// omit a bunch of logic here},setPayPrice: function() {// omit a bunch of logic},setTermTips: function(printable) {// printable}, loadAddress:function(needUpdate) {// omit a bunch of logic here},setAddress: function(addressid) {// omit a bunch of logic}}Copy the code
This UI component exposes very many methods, there is business logic, view of logic, methods and tools, then will bring maintainer is larger, instinctively think that these methods are exposed out is used, so want to refactor some of those methods are some bad laid hands on him, and, in fact, the method of external call is just a show.
A good wrapper, no matter how complex it is inside, will expose the most concise and practical interface, and the internal logic is maintained independently. For example, as a UI component, provide the most basic show/hide method, and if necessary, add the update method to self-update without exposing many details. Cause confusion for callers and maintainers.
4. Fundamental theorem of readability
Basic Theorem of Readability – “Code should be written in such a way as to minimize the time it takes others to understand it.”
Code style and principles not generalize, we often need to choose some coding principle and scheme, such as the trade-off for ternary expression, when we feel both solution point, the only criteria is the basic theorem of readability, no matter how the superb technique of writing, the best code is still let a person can understand the code the first time.
5. Meaningful names
The readability of code is largely dependent on the naming of variables and functions. A good name can help maintainers understand logic in a straightforward way, just like the “style” of writing.
However, it is still difficult to come up with a good name, especially if we are not native English speakers, which adds another layer of obstacles. Some people think that obsessively naming will lead to low efficiency, and the development should be completed in the first place. This is not wrong, we should focus on functional logic in the development process, but do not completely ignore naming, the so-called “writing” is the need to exercise, the more you think, the more naming will be more natural, to later will not affect the efficiency of the work.
Recommend uncle Bob mentioned the boy catch here, each time to see your own code, is a refactoring, changed its name, is the most simple refactorings perhaps think name is nicely at the beginning, but the logic as it is not in conformity with the original name, write when reviewing the code, we can conveniently to rename variables and methods, modern editing tool is easy to do this.
The misericordiam title is the most terrible, as in:
function checkTimeConflict(opts) {
if (opts.param.passcard || (T.bom.get('autopay') && T.bom.get('term_id'))) {
selectToPay({
result: {}
}, opts);
} else {
DB.checkTimeConflict({
param: {
course_id: opts.param.courseId,
term_id: opts.param.termId
},
succ: function(data) {
selectToPay(data, opts);
},
err: function(data) { dealErr(opts, data); }}); }}Copy the code
This function is named at the beginning of check*, which is intended to detect whether the course time conflicts, but the internal logic contains the whole payment process. At this time, for callers, if they do not take a close look at the internal logic, they may mistakenly think that check function has no side effects resulting in accidents.
6. Appropriate comment maintenance
Comments are a controversial topic. Some people believe that readable function variables are clear, do not require additional comments, and annotations are not maintainable, such as:
// 1-PC, 2-Android QH5, 3-Android APP, 4-ios& Non-android QH5, 5-ios APP
var platform = isAndroidApp ? 3 : isIosApp ? 5 : 4;
Copy the code
In fact, the meaning of this field has already changed, but because the modifier only modifies the logic and does not notice this line of comment, the old comment provides error information. At this time, the comment not only becomes invalid, but even leads to the misunderstanding of the maintainer, resulting in the generation of bugs.
For this case, the maintenance notes, or in the comments indicate the interface documentation, maintenance documents, in other cases, the appropriate annotation is necessary, for complex logic, if there is a concise annotation, for help was a great code readability, but some of the unnecessary comments can be removed, the annotation readability is the key of the choice of basic theorem, such as:
const filterFn = (term) => {
if (rule.hideEndTerms && term.is_end) {
return false; // Hide the end of the period}if (rule.hideSignEndTerms && term.is_out_of_date) {
return false; // Hide the end of the registration period}if (rule.hideAppliedTerms && courseUtil.isTermApplied(term)) {
return false; // Hide registered period}if (rule.hideZeroAllowedTerms && courseUtil.isTermZero(term)) {
return false; // Hide the period when the quota is full}if (rule.productType === productType.PACKAGE) {
return false; // Class with hidden curriculum package}return true;
};
Copy the code
For the above logic, although it is possible to roughly guess the functional meaning from the variables, the logical structure can be clearly understood at a glance thanks to the simplicity and clarity of the annotations.
summary
The first three of the six code writing principles mentioned in this paper are biased towards code maintainability, while the last three are biased towards code readability. The whole maintainability and readability constitute the basic quality of code. As a front-end development engineer, if you want to have good code literacy, first of all, you should make your code maintainable, so as not to bring huge costs and workload to the maintenance of others. Secondly, you should try to ensure that the code is beautiful and readable. Clean code is loved by everyone, just like reading a good book, which makes you feel happy.
“Code literacy” is an attitude, and programmers who truly love programming will never lose “code literacy”. We usually referred to as “code” for “programming”, not “programming”, the word “design” reflects our code is a work, maybe not as good as “art” so delicate, but also not is what coarse linen rags, if unconstrained style when writing code, muddle along, holding as long as can realize functions of thought, That the “work” is not has the ornamental value, which not only reflects the author’s “professional”, but also reflects the attitude towards the programming of it, the cleanliness level of the code, maintainability depends on whether you really “care” of your code, every programmer is not necessarily love programming, but you must be in a “serious” attitude towards their own professional.
Uncle Bob, the author of clean Code, describes how someone once gave him a wristband that read “Test Obsessed” and found he couldn’t take it off — not just because it was tight, but because it was a mental straitjacket. When programming, we subconsciously look at our wrists, can we find an invisible wristband?