1 start

This article is a summary of my reading of Clean Code. The original book is based on Java. Here I reorganize some principles which I think are practical and easy to integrate into daily business development. Hopefully, this article can provide some reference for daily development coding and refactoring.

2. Meaningful naming

2.1 Worthy of its name

Variable name to flower mind thought, do not covet convenient, too brief name, after a long time is difficult to read.

// bad
var d = 10;
var oVal = 20;
var nVal = 100;


// good
var days = 10;
var oldValue = 20;
var newValue = 100;Copy the code

2.2 Avoid Misleading

Naming does not confuse information (type, action) about a variable.

Accounts and accountList, unless accountList is really a List, accounts are better than accountList. So don’t use suffixes like List and Map.

// bad
var platformList = {
    web: {},
    wap: {},
    app: {}};// good
var platforms = {
    web: {},
    wap: {},
    app: {}};Copy the code

2.3 Make meaningful distinctions

Express direct differences between variables in a clear sense.

In many cases, there will be product, productData, productInfo and other names. In many cases, there is no obvious difference between Data and Info, so it is better to use product directly.

// bad
var goodsInfo = {
    skuDataList: [],};function getGoods(){};          // Get the list of items
function getGoodsDetail(id){};  // Get a single item by item ID


// good
var goods = {
    skus: [],};function getGoodsList(){};      // Get the list of items
function getGoodsById(id){};    // Get a single item by item IDCopy the code

2.4 Use readable names

Abbreviate to have a degree, such as DAT such writing method, is it DATA or DATE…

// bad
var yyyyMMddStr = eu.format(new Date(), 'yyyy-MM-dd');
var dat = null;
var dev = 'Android';


// good
var todaysDate = eu.format(new Date(), 'yyyy-MM-dd');
var data = null;
var device = 'Android';Copy the code

2.5 Use searchable names

Searchable names can help locate code quickly, especially for some numeric status codes, using enumerations instead of numerical values is recommended.

// bad
var param = {
    periodType: 0};// good
const HOUR = 0, DAY = 1;
var param = {
    periodType: HOUR,
};Copy the code

2.6 Avoid using member prefixes

Make classes and functions small enough to eliminate the need for member prefixes. Because in the long run, prefixes become less and less important in people’s eyes.

2.7 Add meaningful context

Some names may mean different things in different contexts, so it is best to add meaningful context to them.

FirstName, lastName, Street, houseNumber, City, State and Zipcode can be used to determine an address. However, if these variables are taken out separately, the meaning of some variable names is not clear. Context can be added to clarify the meaning, such as addrFirstName, addrLastName, addrState.

Of course, don’t add context arbitrarily, as this will only make variable names long.

// bad
var firsName, lastName, city, zipcode, state;
var sku = {
    skuName: 'sku0'.skuStorage: 'storage0'.skuCost: '10'};// good
var addrFirsName, addrLastName, city, zipcode, addrState;
var sku = {
    name: 'sku0'.storage: 'storage0'.cost: '10'};Copy the code

2.8 Variable names are consistent

Variable names take a little more time. If the object is to be used in multiple functions or modules, use the same variable name. Otherwise, every time you see the object, you will have to re-sort the variable name, causing reading problems.

// bad
function searchGoods(searchText) {
    getList({
        keyword: searchText,
    });
}
function getList(option) {}// good
function searchGoods(keyword) {
    getList({
        keyword: keyword,
    });
}

function getList(keyword) {}Copy the code

3 function

3.1 short

Short is the first rule of functions, and excessively long functions are not only difficult to read but also difficult to maintain. Short, requiring each function to do as little as possible while reducing code nesting and indentation, which can also be difficult to read.

// bad
function initPage(initParams) {
    var data = this.data;
    if ('dimension' in initParams) {
        data.dimension = initParams.dimension;
        data.tab.source.some(function(item, index){
            if(item.value === data.dimension) { data.tab.defaultIndex = index; }}); }if ('standardMedium' in initParams) {
        data.hasStandardMedium = true;
        data.filterParams[data.dimension].standardMedium = initParams.standardMedium;
    }
    if ('plan' in initParams || 'name' in initParams) {
        data.filterParams[data.dimension].planQueryString = initParams.plan || initParams.name;
    } else if ('traceId' in initParams) {
        data.filterParams[data.dimension].planQueryString = 'id:'+ initParams.traceId; }}// good
function initPage(initParams) {
    initDimension(initParams);
    initStandardMedium(initParams);
    initPlanQueryString(initParams);
}
function initDimension(initParams) {
    var data = this.data;
    if ('dimension' in initParams) {
        data.dimension = initParams.dimension;
        data.tab.source.some(function(item, index){
            if(item.value === data.dimension) { data.tab.defaultIndex = index; }}); }}function initStandardMedium(initParams) {
    var data = this.data;
    if ('standardMedium' in initParams) {
        data.hasStandardMedium = true; data.filterParams[data.dimension].standardMedium = initParams.standardMedium; }}function initPlanQueryString() {
    var data = this.data;
    if ('plan' in initParams || 'name' in initParams) {
        data.filterParams[data.dimension].planQueryString = initParams.plan || initParams.name;
    } else if ('traceId' in initParams) {
        data.filterParams[data.dimension].planQueryString = 'id:'+ initParams.traceId; }}Copy the code

3.2 Do only one thing

The function should do one thing, do one thing well, do one thing only.

If a function only does steps at the same abstraction level as the function name, then the function still does only one thing. When something does happen in a function at another level of abstraction, you can split that part into a function at another level, thereby shrinking the function.

When a function can be divided into sections (code blocks), it means that the function is doing too much.

// bad
function onTimepickerChange(type, e) {
    if(type === 'base') {
        // do base type logic...
    } else if (type === 'compare') {
        // do compare type logic...
    }
    // do other stuff...
}

// good
function onBaseTimepickerChange(e) {
    // do base type logic
    this.doOtherStuff();
}

function onCompareTimepickerChange(e) {
    // do compare type logic
    this.doOtherStuff();
}

function doOtherStuff(){}Copy the code

3.3 One level of abstraction for each function

Multiple levels of abstraction should not be mixed up in a function, where steps at the same level are placed in a function, because these steps do the whole thing.

Back to the previously mentioned the problem of variable naming, a variable or function, its scope is wider, the more need a meaningful name to described, readability, reduce when reading the code still need to go to the frequency of the query definition code, when some meaningful names may need more characters, but it was worth it. However, for variables and functions that are used in a small scope, you can shorten the names appropriately. Because the names are too long, they can sometimes make reading difficult.

The hierarchy of abstraction can be divided by the principle of down

A program is like A series of TO paragraphs, each describing the current level and referring TO A subsequent TO paragraph at the next level of abstraction - TO complete A, you need TO complete B, and TO complete C; - To complete B, you need to complete D; - To complete C, you need to complete E;Copy the code

The function name defines its function, obtains a chart and a list, divides the logic of each module in the function, clarifies the division of labor of each function, splits the function name directly indicates the function of each step, does not need additional comment and division. During maintenance, you can quickly locate each step without having to find the corresponding code logic in a long function.

Practical business examples, data Portal – Traffic Kanban – An example of getting trend charts and lists on the right. Select one by TAB to select different indicators, different indicators affect the trend chart and the content of the list on the right, the data of the two modules are merged into one request. The running book can be written as follows, which has several obvious disadvantages:

  • Long. A typical trend chart configuration may take more than 20 lines, and the total function can easily exceed 50 lines.
  • The function name is incorrect. The function name simply indicates that it is fetching a chart, but it is actually fetching the right-hand list data and configuring it;
  • The hierarchy of functions is messy, and it can be divided even more finely;

According to the downward rule

// bad
getChart: function(){
    var data = this.data;
    var option = {
        url: '/chartUrl'.param: {
            dimension: data.dimension,
            period: data.period,
            comparePeriod: data.comparePeriod,
            periodType: data.periodType,
        },
        fn: function(json){
            var data = this.data;
            // Set the chart
            data.chart = json.data.chart;
            data.chart.config = {
                / /... A large number of chart configurations, perhaps more than 20 lines long
            }
            // Set the list on the rightdata.sideList = json.data.list; }};// Get the request parameters
    this.fetchData(option);
},

// good
getChartAndSideList: function(){
    var option = {
        url: '/chartUrl'.param: this.getChartAndSideListParam();
        fn: function(json){
            this.setChart(json);
            this.setSideList(json); }};this.fetchData(option);
},Copy the code

3.4 switch statement

Switch statements make code very long because switch statements are designed to do more than one thing, and as state increases, so do switch statements. Therefore, it is possible to replace the switch statement, or to place it at a lower level.

Put at the bottom of the meaning, can be understood as buried in the abstract factory underground, using the abstract factory to return a different content of the method or object for processing.

3.5 Reduce the parameters of the function

The more arguments a function has, the longer the comment is written, and the easier it is to misplace the arguments. When a function has too many arguments, consider passing them in as argument lists or objects.

An example from a data portal:

// bad
function getSum(a [, b, c, d, e ...]){}


// good
function getSum(arr){}Copy the code
// bad
function exportExcel(url, param, onsuccess, onerror){}


// good
/** * @param option * @property url * @property param * @property onsucces * @property onerror */
function exportExcel(option){}Copy the code

Minimize the number of parameters, preferably no more than 3

3.6 Choose a good name

The function should have a better name, and the use of verbs and keywords can improve the readability of the function. Such as:

A function within a certain range is called within. It is easy to determine the function’s function from the name, but it is still not the best, because the function has three parameters, you cannot see the relationship between the three parameters of the function at a glance, b <= a && a<= c, Or a <= b&&b <= c?

It may be possible to change the parameter name to express the relationship between the three parameters, but it is not possible to know how to use the function until you see its definition.

If you change the name again, you can easily see the relationship between the three arguments. Of course, the name can be very long, but if the function needs to be used on a large scale, the longer name is worth the trade-off for better readability.

// bad
function within(a, b, c){}

// good
function assertWithin(val, min, max){}

// good
function assertValWithinMinAndMax(val, min, max){}Copy the code

3.7 No Side effects

A function that has side effects is usually impure, which means that the function does more than one thing. The side effects of the function are hidden, and the function caller cannot tell what the function is doing directly from the function name.

4 comments

4.1 good comments

Legal information, comments providing information, explanation of intent, interpretation, warning, TODO, amplification (amplifying the importance of some seemingly irrational code), public API comments

Try to make functions and variables become graduated, do not rely on comments to describe, for the complicated part is appropriate to use comments.

4.2 a bad comment

Mumbling, redundant comments (for example, function names are supposed to explain the intent), misleading comments, canonical comments (for the sake of specification, function and parameter names are already clear information), journaling comments (for logging useless changes), nonsense comments

4.3 the principle of

  1. Don’t use comments when you can use functions or variables, which means taking the time to come up with a good name
// bad
var d = 10;     / / number of days

// good
var days = 10;Copy the code
  1. Don’t leave out commented code; important code is not commented out

Data portal – real-time situation inside a piece of code, / SRC/javascript/realTimeOverview/components/index. Js

// bad
function dimensionChanged(dimension){
    var data = this.data.keyDealComposition;
    data.selectedDimension = dimension;
    // 2016.10.31 MODIFY: Product change, do not display secondary category when selecting brand distribution
    // if (dimension.dimensionId == '6') {
    // data.columns[0][0].name = dimension.dimensionName;
    // data.columns[0]. Splice (1, 0, {name:' categoryName ', value:' categoryName ', noSort: true});
    // } else {
        this.handle('util.setTableHeader');
    // }
    this.handle('refreshComposition');
};

// good
function dimensionChanged(dimension){
    var data = this.data.keyDealComposition;
    data.selectedDimension = dimension;
    this.handle('util.setTableHeader');
    this.handle('refreshComposition');
};Copy the code
  1. Don’t put too much information in comments. No one will read them

  2. Non-public functions, there is no need to add excessive comments, redundant comments will make the code less compact, increase the reading barrier

// bad
/** * sets the table header */
function setTableHeader(){},

// good
function setTableHeader(){},Copy the code
  1. Comments after parentheses
// bad
function doSomthing(){
    while(! buffer.isEmpty()) {// while 1
        // ...
        while(arr.length > 0) {  // while 2
            // ...
            if() {}}// while 2
    } // while 1
}Copy the code
  1. No logging, no attribution, no version control
// bad
/** * 2016.12.03 bugfix, by XXXX * 2016.11.01 new feature, by XXXX * 2016.09.12 new feature, by XXXX *... * /


// bad
/** * created by xxxx * modified by xxxx */
function addSum() {}

/** * created by xxxx */
function getAverage() {
    // modified by xxx
}Copy the code
  1. Try not to use location markers
// bad

/*************** Filters ****************/

///////////// Initiation /////////////////Copy the code

5 format

5.1 Vertical Direction

  1. The relevant code is shown compactly, with different parts separated by Spaces
// bad
function init(){
    this.data.chartView = this.$refs.chartView;
    this.$parent.$on('inject'.function () {
        this.dataConvert(this.data.source);
        this.draw();
    });
    this.$watch('source'.function (newValue, oldValue) {
        if(newValue && newValue ! =this.data.initValue) {
            this.dataConvert(newValue);
            this.draw();
        } else if(! newValue) {if (self.data.chartView) {
                this.data.chartView.innerHTML = ' '; }}},true);
}

// good
function init(){
    this.data.chartView = this.$refs.chartView;

    this.$parent.$on('inject'.function () {
        this.dataConvert(this.data.source);
        this.draw();
    });

    this.$watch('source'.function (newValue, oldValue) {
        if(newValue && newValue ! =this.data.initValue) {
            this.dataConvert(newValue);
            this.draw();
        } else if(! newValue) {if (this.data.chartView) {
                this.data.chartView.innerHTML = ' '; }}},true);
}Copy the code
  1. Don’t clog your code with too many long comments
// bad
BaseComponent.extend({
    checkAll: function(status){ status = !! status;var data = this.data;
        this.checkAllList(status);
        this.checkSigList(status);
        data.checked.list = [];
        if(status){
            // Clear the list when selecting all, then add the selected items using array.push
            // Check. List = dataList
            // Check. List is the same as dataList
            // Use push to solve this problem
            data.sigList.forEach(function(item,i){ data.checked.list.push(item.data.item); })}this.$emit('check', {
            sender: this.index: CHECK_ALL,
            checked: status, }); }});// good
BaseComponent.extend({
    checkAll: function(status){ status = !! status;this.checkAllList(status);
        this.checkSigList(status);
        this.clearCheckedList();
        if(status){
            this.updateCheckedList();
        }

        this.emitCheckEvent(CHECK_ALL, status); }});Copy the code
  1. Functions are laid out in dependency order, and the called function should follow the calling function
// bad
function updateModule() {}
function updateFilter() {}
function reset() {}
function refresh() {
    updateFilter();
    updateModule();
}

// good
function refresh() {
    updateFilter();
    updateModule();
}
function updateFilter() {}
function updateModule() {}
function reset() {}Copy the code
  1. Related, similar functions are put together
// bad
function onSubmit() {}
function refresh() {}
function onFilterChange() {}
function reset() {}

// good
function onSubmit() {}
function onFilterChange() {}

function refresh() {}
function reset() {}Copy the code
  1. Variable declarations are close to where they are used
// bad
function (x){
    var a = 10, b = 100;
    var c, d;

    a = (a-b) * x;
    b = (a-b) / x;
    c = a + b;
    d = c - x;
}

// good
function (x){
    var a = 10, b = 100;

    a = (a-b) * x;
    b = (a-b) / x;

    var c = a + b;
    var d = c - x;
}Copy the code

5.2 Horizontal Direction

  1. Spaces between symbols, but take care of precedence
// bad
var v = a + (b + c) / d + e * f;

// good
var v = a + (b+c)/d + e*f;Copy the code
  1. Horizontal alignment of variables makes little sense and should be kept close
// bad
var a       = 1;
var sku     = goodsInfo.sku;
var goodsId = goodsInfo.goodsId;

// good
var a = 1;
var sku = goodsInfo.sku;
var goodsId = goodsInfo.goodsId;Copy the code

5.4 For short if and while statements, try to keep indentation

It’s easy to be fooled by reading habits by suddenly changing the indentation pattern

// bad
if(empty){return; }// good
if(empty){
    return;
}

// bad
while(cli.readCommand() ! =- 1);
app.run();


// good
while(cli.readCommand() ! =- 1); app.run();Copy the code

6 application in actual business code

Huge config function

For more complex components or page components, many properties need to be defined, initialized and listened to, as shown in the following code. I’ve seen similar code in several large pages, with the Config method having as few as 100 lines and as many as 400 lines.

The Config method is basically an entry point to a component and is usually read first for maintenance purposes, but with a function this long, it’s easy to get blindfolded at first glance.

Component.extend({
    template: tpl,
    config: function(data){
        eu.extend(data, {
            tabChartTab: 0.periodType: 0.dimensionType: 1.dealConstituteCompare:false.dealConstituteSort: {
                dimensionValue: 'sales'.sortType: 0,},dealConstituteDecorate: {
                noCompare: [].progress: ['salesPercent'].sort: []},defaultMetrics: []./ /... There are hundreds of lines below about other module properties, flow, hotSellRank, etc
        });

        this.$watch('periodType'.function(){
            // ...
        });

        this.$watch('topCategoryId'.function(){
            // ...
        });

        // There is also part of the asynchronous request code...
        this.refresh(); }})Copy the code

The obvious disadvantages of this code are:

  • It is too long
  • Variable names have redundant information and poor searchability
  • Too many variables (attributes)
  • Too many things to do, initialize component properties, add listener methods, and some business logic code

This could make some improvements to these:

  • Use enumerations instead of numbers
  • Only the direct initialization code for all range-increasing attributes is retained in config; the rest of the module-specific attributes are calledinitDataMethod to initialize
  • initDataThe initialization method is further divided by module
  • Attributes belonging to each module are divided into the same object, which reduces the number of attributes mounted on the component and simplifies the naming of attributes
  • The listening method is also passedaddWatchersInitialize the
  • Some of the logic that needs to be executed during initialization should be placed as far as possibleinitAfter the component is instantiated
const TAB_A = 0, TAB_B = 1;
const HOUR = 0, DAY = 1;
const DIMENSION_A = 0, DIMENSION_B = 1;
const DISABLE = false, ENABLE = true;

Component.extend({
    template: tpl,
    config: function(data){
        eu.extend(data, {
            tabChartTab: TAB_A,
            periodType: HOUR,
            dimensionType: DIMENSION_B,
        });

        this.initData();
        this.addWatchers();
    },

    initData: function(){
        this.initDealConsitiuteData();
        this.initFlowData();
        this.initHotSellRank();
    },

    initDealConsitiuteData: function(){
        this.data.dealConstitute = {
            compare: DISABLE,
            sort: {
                dimensionValue: 'sales'.sortType: 0,},decorate: {
                noCompare: [].progress: ['salesPercent'].sort: []},defaultMetrics: [],}},addWatchers: function(){
        this.$watch('periodType'.function(){
            // ...
        });

        this.$watch('topCategoryId'.function(){
            // ...
        });
    },

    init: function(){
        // Partially initializes the logic to execute
        this.refresh(); }})Copy the code

In fact, the code readability is improved by following the above optimization, but since this is a page component and there are many lines of code, the modified method is much more and still not easy to read. Therefore, for such a large page, it is more appropriate to split the page into several modules, split the business logic, reduce the amount of code in each module, and improve the readability. For non-detachable components or modules that still contain a large number of attributes that need to be initialized, the examples above can be used as a reference.

7 summary

Several key points sorted out in this paper:

  • Writing code is like writing a story, in which the names of the characters (variables, functions) must be well read.
  • The function should be short and not mix too much irrelevant, different levels of logic;
  • Notes to be concise and accurate, can not write do not write;
  • Code layout to learn from the newspaper, pay attention to the vertical and horizontal layout of the spacing, tightly connected layout to compact;

It’s hard for even the most experienced to write clean code the first time, so be diligent about refactoring your code and making changes as you go. Code only becomes concise and refined after it has been modified and refined over and over again.

8 reference

  1. Clean Code
  2. clean-code-javascript