Контент из "аккуратного кода"Alex KondovБоуэнtao-of-reactи «Чистый код JavaScript»
- Ясное мышление, уменьшить вероятность ошибок
- Легче поддерживать и облегчать совместную работу в команде
- Выглядеть комфортно и повысить эффективность
- ......
Качество программного обеспечения пропорционально чистоту кода --Robert.c.martin
Иерархия программного обеспечения 3R:readable, reusable, and refactorableЧитаемость, повторное использование, рефакторинг
Следующие принципы являются одними из лучших практик, предложенных автором, но не применяемых на практике.
О нейминге
1. Используйте осмысленные и читаемые имена переменных
👎 const yyyymmdstr = moment().format("YYYY/MM/DD");
👍 const currentDate = moment().format("YYYY/MM/DD");
2. Используйте значимые переменные вместо индексов массива
👎
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
address.match(cityZipCodeRegex)[1],
address.match(cityZipCodeRegex)[2]
);
👍
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
3. Имя переменной должно быть лаконичным, не прикрепляйте бесполезную информацию
👎
const Car = {
carMake: "Honda",
carModel: "Accord",
carColor: "Blue"
};
function paintCar(car, color) {
car.carColor = color;
}
👍
const Car = {
make: "Honda",
model: "Accord",
color: "Blue"
};
function paintCar(car, color) {
car.color = color;
}
4. Устранение волшебной строки
👎 setTimeout(blastOff, 86400000);
👍 const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000;
setTimeout(blastOff, MILLISECONDS_PER_DAY);
5. Используйте аргументы по умолчанию вместо операторов короткого замыкания
👎
function createMicrobrewery(name) {
const breweryName = name || "Hipster Brew Co.";
// ...
}
👍
function createMicrobrewery(name = "Hipster Brew Co.") {
// ...
}
О функциях
1. Сделайте ли одну вещь в функции, есть преимущество в простоте, легко понять, легко проверить.
👎
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
👍
function emailActiveClients(clients) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
---------------------分割线-----------------------
👎
function createFile(name, temp) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
👍
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile(`./temp/${name}`);
}
- Деструктуризация клонирует значение объекта параметра, переданного в функцию (поверхностное клонирование), помогая предотвратить побочные эффекты.
- Линтер может подсказать, какие параметры не были использованы
👎
function createMenu(title, body, buttonText, cancellable) {
// ...
}
createMenu("Foo", "Bar", "Baz", true);
👍
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
});
3. Имя функции должно напрямую отражать назначение функции
👎
function addToDate(date, month) {
// ...
}
const date = new Date();
// It's hard to tell from the function name what is added
addToDate(date, 1);
👍
function addMonthToDate(month, date) {
// ...
}
const date = new Date();
addMonthToDate(1, date);
4. Не используйте слишком много уровней абстракции для функции.Если ваша функция делает слишком много, вам нужно разделить ее на несколько функций.
👎
function parseBetterJSAlternative(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
// ...
});
});
const ast = [];
tokens.forEach(token => {
// lex...
});
ast.forEach(node => {
// parse...
});
}
👍
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach(node => {
// parse...
});
}
function tokenize(code) {
const REGEXES = [
// ...
];
const statements = code.split(" ");
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
tokens.push(/* ... */);
});
});
return tokens;
}
function parse(tokens) {
const syntaxTree = [];
tokens.forEach(token => {
syntaxTree.push(/* ... */);
});
return syntaxTree;
}
5. Уменьшите повторяющийся код
👎
function showDeveloperList(developers) {
developers.forEach(developer => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers) {
managers.forEach(manager => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
👍
function showEmployeeList(employees) {
employees.forEach(employee => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const data = {
expectedSalary,
experience
};
switch (employee.type) {
case "manager":
data.portfolio = employee.getMBAProjects();
break;
case "developer":
data.githubLink = employee.getGithubLink();
break;
}
render(data);
});
}
6. Попробуйте использовать чистые функции (функциональное программирование, а не императивное программирование)
👎
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}
👍
const programmerOutput = [
{
name: "Uncle Bobby",
linesOfCode: 500
},
{
name: "Suzie Q",
linesOfCode: 1500
},
{
name: "Jimmy Gosling",
linesOfCode: 150
},
{
name: "Gracie Hopper",
linesOfCode: 1000
}
];
const totalOutput = programmerOutput.reduce(
(totalLines, output) => totalLines + output.linesOfCode,
0
);
7. Помните о побочных эффектах функции
👎
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
👍
const addItemToCart = (cart, item) => {
return [...cart, { item, date: Date.now() }];
};
8. Не переоптимизируйте
Современные браузеры выполняют большую оптимизацию во время выполнения. Много раз, если вы оптимизируете, это пустая трата времени.
👎
// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
👍
for (let i = 0; i < list.length; i++) {
// ...
}
Об аннотациях
1.Comments are an apology, not a requirement. Good code mostly documents itself.
Хороший код самокомментирует
👎
function hashIt(data) {
// The hash
let hash = 0;
// Length of string
const length = data.length;
// Loop through every character in data
for (let i = 0; i < length; i++) {
// Get character code.
const char = data.charCodeAt(i);
// Make the hash
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
👍
function hashIt(data) {
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = (hash << 5) - hash + char;
// Convert to 32-bit integer
hash &= hash;
}
}
2. Не пишите в комментариях, что умеет git
👎
/**
* 2016-12-20: Removed monads, didn't understand them (RM)
* 2016-10-01: Improved using special monads (JP)
* 2016-02-03: Removed type-checking (LI)
* 2015-03-14: Added combine with type-checking (JR)
*/
function combine(a, b) {
return a + b;
}
👍
function combine(a, b) {
return a + b;
}
О компонентах
1. Используйте функциональные компоненты, когда это возможно
У функциональных компонентов более простой синтаксис, нет функции жизненного цикла, конструктора. Та же логика и надежность, функциональные компоненты можно сделать меньшим количеством кода.
👎
class Counter extends React.Component {
state = {
counter: 0,
}
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ counter: this.state.counter + 1 })
}
render() {
return (
<div>
<p>counter: {this.state.counter}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
)
}
}
👍
function Counter() {
const [counter, setCounter] = useState(0)
handleClick = () => setCounter(counter + 1)
return (
<div>
<p>counter: {counter}</p>
<button onClick={handleClick}>Increment</button>
</div>
)
}
2. Переведите логический код из компонентов функций
Максимально отделяйте логику от компонента, а нужные значения передавайте в служебную функцию в виде параметров. Организация вашей логики вне функциональных компонентов упрощает отслеживание ошибок и расширение функциональности.
👎
export default function Component() {
const [value, setValue] = useState('')
function isValid() {
// ...
}
return (
<>
<input
value={value}
onChange={e => setValue(e.target.value)}
onBlur={validateInput}
/>
<button
onClick={() => {
if (isValid) {
// ...
}
}}
>
Submit
</button>
</>
)
}
👍
function isValid(value) {
// ...
}
export default function Component() {
const [value, setValue] = useState('')
return (
<>
<input
value={value}
onChange={e => setValue(e.target.value)}
onBlur={validateInput}
/>
<button
onClick={() => {
if (isValid(value)) {
// ...
}
}}
>
Submit
</button>
</>
)
}
3. Контролируйте длину компонентов и уменьшите связь с пользовательским интерфейсом
Функциональные компоненты также являются функциями, и их длина также должна контролироваться.Если компонент слишком длинный, его необходимо разделить на несколько компонентов.
👎
function Filters({ onFilterClick }) {
return (
<>
<p>Book Genres</p>
<ul>
<li>
<div onClick={() => onFilterClick('fiction')}>Fiction</div>
</li>
<li>
<div onClick={() => onFilterClick('classics')}>
Classics
</div>
</li>
<li>
<div onClick={() => onFilterClick('fantasy')}>Fantasy</div>
</li>
<li>
<div onClick={() => onFilterClick('romance')}>Romance</div>
</li>
</ul>
</>
)
}
// 👍 Use loops and configuration objects
const GENRES = [
{
identifier: 'fiction',
name: Fiction,
},
{
identifier: 'classics',
name: Classics,
},
{
identifier: 'fantasy',
name: Fantasy,
},
{
identifier: 'romance',
name: Romance,
},
]
function Filters({ onFilterClick }) {
return (
<>
<p>Book Genres</p>
<ul>
{GENRES.map(genre => (
<li>
<div onClick={() => onFilterClick(genre.identifier)}>
{genre.name}
</div>
</li>
))}
</ul>
</>
)
}
4. Старайтесь избегать переопределения функциональных компонентов внутри функциональных компонентов.
Не пишите другой функциональный компонент внутри функционального компонента. Функциональный компонент должен быть просто функцией. Переопределение функционального компонента внутри функционального компонента означает, что внутренний функциональный компонент может получить доступ ко всем состояниям и реквизитам внешнего компонента через область видимости, что сделает внутренне определенный компонент ненадежным. Переместите внутренние компоненты во внешние, избегая эффектов замыканий и областей видимости.
// 👎 Don't write nested render functions
function Component() {
function renderHeader() {
return <header>...</header>
}
return <div>{renderHeader()}</div>
}
// 👍 Extract it in its own component
import Header from '@modules/common/components/Header'
function Component() {
return (
<div>
<Header />
</div>
)
}
5. Оптимизируйте реквизит
- Как контролировать количество реквизита — вопрос спорный. Но чем больше props передает компонент, тем больше он делает — это консенсус. Когда реквизиты достигают определенного числа, это означает, что компонент делает слишком много. Когда количество реквизитов достигает более 5, компонент необходимо разделить. В некоторых крайних случаях, таких как компоненты входного типа, может быть слишком много реквизита, но в целом 5 реквизитов могут удовлетворить потребности большинства компонентов.
Совет: чем больше реквизитов у компонента, тем легче его перерендерить.
- Использование сокращенного синтаксиса для условного рендеринга может привести к непредвиденным проблемам в некоторых сценариях, возможно, к рендерингу 0 на интерфейсе. Чтобы этого избежать, попробуйте использовать тернарный оператор. В то время как оператор короткого замыкания поддерживает чистоту кода, тернарный оператор гарантирует правильное отображение.
// 👎 Try to avoid short-circuit operators
function Component() {
const count = 0
return <div>{count && <h1>Messages: {count}</h1>}</div>
}
// 👍 Use a ternary instead
function Component() {
const count = 0
return <div>{count ? <h1>Messages: {count}</h1> : null}</div>
}
О других
1. Поместите компоненты в отдельные папки
// 👎 Don't keep all component files together
├── components
├── Header.jsx
├── Header.scss
├── Header.test.jsx
├── Footer.jsx
├── Footer.scss
├── Footer.test.jsx
// 👍 Move them in their own folder
├── components
├── Header
├── index.js
├── Header.jsx
├── Header.scss
├── Header.test.jsx
├── Footer
├── index.js
├── Footer.jsx
├── Footer.scss
├── Footer.test.jsx
2. Попробуйте использовать абсолютные пути
Использование абсолютных путей позволяет перемещать один файл с минимальными изменениями в других файлах. Абсолютные пути также позволяют сразу увидеть, откуда берутся все зависимые файлы.
(над)