Preface
In our normal work development, most of them are public projects developed by adults. When we develop code coding, we consider the readability, reusability and extensibility of code.
Clean code is not only reliable in quality, but also lays a good foundation for later maintenance and upgrading.
We will discuss from the following aspects:
variable
1. Variable Naming
Generally, we define variables by using meaningful vocabulary commands, and by meeting people.
//bad code const yyyymmdstr = moment().format('YYYY/MM/DD'); //better code const currentDate = moment().format('YYYY/MM/DD');
2. Descriptible
Generating a new variable through a variable also requires naming the new variable, which means that each variable knows what it is when you see it at first glance.
//bad code const ADDRESS = 'One Infinite Loop, Cupertino 95014'; const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode(ADDRESS.match(CITY_ZIP_CODE_REGEX)[1], ADDRESS.match(CITY_ZIP_CODE_REGEX)[2]); //better code const ADDRESS = 'One Infinite Loop, Cupertino 95014'; const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; const [, city, zipCode] = ADDRESS.match(CITY_ZIP_CODE_REGEX) || []; saveCityZipCode(city, zipCode);
3. Nomenclature of Formal Parameters
In the loop for, for Each, map, we need to name directly
//bad code const locations = ['Austin', 'New York', 'San Francisco']; locations.map((l) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... // You need to look at other code to determine what'l'does. dispatch(l); }); //better code const locations = ['Austin', 'New York', 'San Francisco']; locations.forEach((location) => { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch(location); });
4. Avoid meaningless prefixes
For example, if we only create an object, there is no need to add the attribute of each object to the object name.
//bad code const car = { carMake: 'Honda', carModel: 'Accord', carColor: 'Blue' }; function paintCar(car) { car.carColor = 'Red'; } //better code const car = { make: 'Honda', model: 'Accord', color: 'Blue' }; function paintCar(car) { car.color = 'Red'; }
5. Default value
//bad code function createMicrobrewery(name) { const breweryName = name || 'Hipster Brew Co.'; // ... } //better code function createMicrobrewery(name = 'Hipster Brew Co.') { // ... }
function
1, parameters
If there are many general parameters, ES6 should be used to deconstruct and transmit parameters.
//bad code function createMenu(title, body, buttonText, cancellable) { // ... } //better code function createMenu({ title, body, buttonText, cancellable }) { // ... } //better code createMenu({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true });
2. Simplification
It's better to do only one thing in a method and not deal with it too much, so that the code is very readable.
//bad code function emailClients(clients) { clients.forEach((client) => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); } //better code function emailActiveClients(clients) { clients .filter(isActiveClient) .forEach(email); } function isActiveClient(client) { const clientRecord = database.lookup(client); return clientRecord.isActive(); }
3. Object Setting Default Properties
//bad code const menuConfig = { title: null, body: 'Bar', buttonText: null, cancellable: true }; function createMenu(config) { config.title = config.title || 'Foo'; config.body = config.body || 'Bar'; config.buttonText = config.buttonText || 'Baz'; config.cancellable = config.cancellable !== undefined ? config.cancellable : true; } createMenu(menuConfig); //better code const menuConfig = { title: 'Order', // 'body'key missing buttonText: 'Send', cancellable: true }; function createMenu(config) { config = Object.assign({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true }, config); // Configuration becomes: {title:'Order', body:'Bar', buttonText:'Send', cancellable: true} // ... } createMenu(menuConfig);
4. Avoiding side effects
Function receives a value and returns a new value. In addition, we call it side effects, such as modifying global variables, IO operations on files, etc.
When functions do need side effects, such as IO operations on files, do not use multiple functions/classes for file operations, and only one function/class for file operations. That is to say, side effects need to be dealt with in only one place.
Three major sinkholes of side effects: random modification of variable data types, random sharing of states without data structures, and no handling of side effects in a unified place.
//bad code // Global variables are referenced by a function // Now that this variable has changed from a string to an array, unforeseen errors can occur if there are other function references. var name = 'Ryan McDermott'; function splitIntoFirstAndLastName() { name = name.split(' '); } splitIntoFirstAndLastName(); console.log(name); // ['Ryan', 'McDermott']; //better code var name = 'Ryan McDermott'; var newName = splitIntoFirstAndLastName(name) function splitIntoFirstAndLastName(name) { return name.split(' '); } console.log(name); // 'Ryan McDermott'; console.log(newName); // ['Ryan', 'McDermott'];
In JavaScript, basic types are passed by assignment, and objects and arrays are passed by reference. Take reference passing as an example:
Suppose we write a shopping cart, add items to the cart through the addItemToCart() method, and modify the cart array. At this point, the purchase() method is invoked to purchase, and the acquired shopping cart array is the latest data due to reference passing.
It looks all right, doesn't it?
If the network fails when the user clicks on the purchase, the purchase() method is repeatedly invoked, at the same time the user adds new goods, and the network is restored. So the purchase() method is wrong to get an array of shopping carts.
To avoid this problem, we need to clone the shopping cart array and return the new array every time we add a new item.
//bad code const addItemToCart = (cart, item) => { cart.push({ item, date: Date.now() }); }; //better code const addItemToCart = (cart, item) => { return [...cart, {item, date: Date.now()}] };
5. Global approach
In JavaScript, you should never pollute the whole world and create unpredictable bug s in the production environment. For example, you add a diff method to Array.prototype to determine the difference between the two arrays. Your colleague is going to do something similar, but his diff method is used to determine the difference between the first elements of two arrays. It's obvious that your approach will conflict, and we can extend Array with ES2015/ES6 grammar when we encounter this kind of problem.
//bad code Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); }; //better code class SuperArray extends Array { diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); } }
6. Avoid type checking
JavaScript is typeless, meaning that you can pass any type of parameter. This degree of freedom can easily be confusing, and you will check the type unconsciously. Think about it carefully. Do you really need to check the type or do you have problems with your API design?
//bad code function travelToTexas(vehicle) { if (vehicle instanceof Bicycle) { vehicle.pedal(this.currentLocation, new Location('texas')); } else if (vehicle instanceof Car) { vehicle.drive(this.currentLocation, new Location('texas')); } } //better code function travelToTexas(vehicle) { vehicle.move(this.currentLocation, new Location('texas')); }
If you need to do static type checking, such as strings, integers, etc., recommend using TypeScript, otherwise your code will become stinky and long.
//bad code function combine(val1, val2) { if (typeof val1 === 'number' && typeof val2 === 'number' || typeof val1 === 'string' && typeof val2 === 'string') { return val1 + val2; } throw new Error('Must be of type String or Number'); } //better code function combine(val1, val2) { return val1 + val2; }
Judgment of Complex Conditions
When we write js code, we often encounter the situation of complex logic judgment. Generally, you can use if/else or switch to realize multiple conditional judgment. But there will be a problem. With the increase of logic complexity, if/else/switch in the code will become more and more bloated, more and more incomprehensible, so how to write more elegant judgment Broken logic
1,if/else
Click List Button Event
/** * Button Click Event * @param {number} status Activity Status: 1. In the process of opening a group, 2. Failure in opening a group, 3. Sale of goods, 4. Success in opening a group, 5. System Cancellation */ const onButtonClick = (status)=>{ if(status == 1){ sendLog('processing') jumpTo('IndexPage') }else if(status == 2){ sendLog('fail') jumpTo('FailPage') }else if(status == 3){ sendLog('fail') jumpTo('FailPage') }else if(status == 4){ sendLog('success') jumpTo('SuccessPage') }else if(status == 5){ sendLog('cancel') jumpTo('CancelPage') }else { sendLog('other') jumpTo('Index') } }
As we can see from the above, we can do different things through different states. The code looks very ugly. You can easily propose a rewriting scheme for this code. switch comes out.
2,switch/case
/** * Button Click Event * @param {number} status Activity Status: 1. In the process of opening a group, 2. Failure in opening a group, 3. Sale of goods, 4. Success in opening a group, 5. System Cancellation */ const onButtonClick = (status)=>{ switch (status){ case 1: sendLog('processing') jumpTo('IndexPage') break case 2: case 3: sendLog('fail') jumpTo('FailPage') break case 4: sendLog('success') jumpTo('SuccessPage') break case 5: sendLog('cancel') jumpTo('CancelPage') break default: sendLog('other') jumpTo('Index') break } }
This looks much clearer than if/else. Careful students also find some tricks. When case 2 and case 3 are the same logic, they can omit executing statements and break s, and case 2 automatically executes case 3 logic.
3. Store in Object
The judgement condition is regarded as the attribute name of the object and the processing logic is regarded as the attribute value of the object. When the button is clicked, the logical judgement is made by searching the attribute of the object, which is especially suitable for the case of unary condition judgement.
const actions = { '1': ['processing','IndexPage'], '2': ['fail','FailPage'], '3': ['fail','FailPage'], '4': ['success','SuccessPage'], '5': ['cancel','CancelPage'], 'default': ['other','Index'], } /** * Button Click Event * @param {number} status Activity Status: 1. In the process of opening a group, 2. Failure in opening a group, 3. Sale of goods, 4. Success in opening a group, 5. System Cancellation */ const onButtonClick = (status)=>{ let action = actions[status] || actions['default'], logName = action[0], pageName = action[1] sendLog(logName) jumpTo(pageName) }
4. Store in Map
const actions = new Map([ [1, ['processing','IndexPage']], [2, ['fail','FailPage']], [3, ['fail','FailPage']], [4, ['success','SuccessPage']], [5, ['cancel','CancelPage']], ['default', ['other','Index']] ]) /** * Button Click Event * @param {number} status Activity Status: 1. In the process of opening a group, 2. Failure in opening a group, 3. Sale of goods, 4. Success in opening a group, 5. System Cancellation */ const onButtonClick = (status)=>{ let action = actions.get(status) || actions.get('default') sendLog(action[0]) jumpTo(action[1]) }
Isn't it better to use the Map object in es6 in this way? What's the difference between a Map object and an Object object?
- An object usually has its own prototype, so an object always has a "prototype" key.
- The key of an object can only be a string or Symbols, but the key of a Map can be any value.
- You can easily get the number of key-value pairs of a Map by using the size attribute, while the number of key-value pairs of objects can only be confirmed manually.
Code style
Constant capitalization
//bad code const DAYS_IN_WEEK = 7; const daysInMonth = 30; const songs = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; const Artists = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restore_database() {} class animal {} class Alpaca {} //better code const DAYS_IN_WEEK = 7; const DAYS_IN_MONTH = 30; const SONGS = ['Back In Black', 'Stairway to Heaven', 'Hey Jude']; const ARTISTS = ['ACDC', 'Led Zeppelin', 'The Beatles']; function eraseDatabase() {} function restoreDatabase() {} class Animal {} class Alpaca {}
Declare before call
//bad code class PerformanceReview { constructor(employee) { this.employee = employee; } lookupPeers() { return db.lookup(this.employee, 'peers'); } lookupManager() { return db.lookup(this.employee, 'manager'); } getPeerReviews() { const peers = this.lookupPeers(); // ... } perfReview() { this.getPeerReviews(); this.getManagerReview(); this.getSelfReview(); } getManagerReview() { const manager = this.lookupManager(); } getSelfReview() { // ... } } const review = new PerformanceReview(employee); review.perfReview(); //better code class PerformanceReview { constructor(employee) { this.employee = employee; } perfReview() { this.getPeerReviews(); this.getManagerReview(); this.getSelfReview(); } getPeerReviews() { const peers = this.lookupPeers(); // ... } lookupPeers() { return db.lookup(this.employee, 'peers'); } getManagerReview() { const manager = this.lookupManager(); } lookupManager() { return db.lookup(this.employee, 'manager'); } getSelfReview() { // ... } } const review = new PerformanceReview(employee); review.perfReview();