Perevod knigi Raiana Makdermota clean-code-javascript.
Oglavlenie
- Vvedenie
- Peremennye
- Funktsii
- Ob'ekty i struktury dannykh
- Klassy
- Testirovanie
- Asinkhronnost'
- Obrabotka oshibok
- Formatirovanie
- Kommentarii
- Perevod
Vvedenie
Inzhenernye printsipy programmnogo obespecheniia, iz knigi Roberta S. Martina Clean Code, prisposoblennye dlia JavaScript.
Eto ne stail gaid. Eto rukovodstvo po napisaniiu chitaemogo, pereispol'zuemogo i podderzhivaemogo koda na javascript. Ne kazhdyi printsip zdes', dolzhen strogo sobliudat'sia. Eto printsipy i nichego bol'she, no oni sformirovany v techenie mnogikh let kollektivnogo opyta avtorami clean code.
Nashemu remeslu inzhenerii programmnogo obespecheniia chut' bol'she 50 let, i my vse eshchio mnogo chemu uchimsia. Kogda programmnaia arkhitektura stanet stara kak sama arkhitektura, mozhet byt', togda my budem imet' bolee zhestkie pravila, kotorym neobkhodimo budet sledovat'. Na dannyi moment, pust' eti printsipy sluzhat kamnem pretknoveniia, s pomoshch'iu kotorogo vy budete otsenivat' kachestvo koda JavaScript, kotoryi pishete vy i vasha komanda.
Eshche odna veshch': znanie etikh printsipov ne sdelaet vas srazu luchshim razrabotchikom programmnogo obespecheniia, i esli vy budete priderzhivat'sia etikh printsipov mnogo let ne oznachaet, chto vy ne budete delat' oshibki. Kazhdyi fragment koda nachinaetsia kak chernovik, kak i kusok mokroi gliny kotoryi priobretaet svoiu okonchatil'nuiu formu. Nakonets, my ispravliaem nedostatki, kogda rassmatrivaem kod so svoimi kollegami. Ne korite sebia pri pervykh nabroskakh koda, kotorye nuzhdaiutsia v uluchshenii. Uluchshaite svoi kod vmesto etogo.
Peremennye
Ispol'zuite znachimye i proiznosimye imena peremennykh
Plokho:
Khorosho:
vernut'sia
Ispol'zuite odin i tot zhe metod dlia togo zhe tipa peremennoi
Plokho:
getClientData();
getCustomerRecord();
Khorosho:
vernut'sia
Ispol'zuite imenovannye znacheniia
My budem chitat' kod chashche, chem my kogda-nibud' napishem. Vazhno pisat' chitaemyi kod, kotoryi legko iskat'. Delaite vashi imena dlia poiska. Takie instrumenty, kak buddy.js i ESLint mogut pomoch' identifitsirovat' nenazvannye konstanty.
Plokho:
setTimeout(blastOff, 86400000);
Khorosho:
const MILLISECONDS_IN_A_DAY = 86400000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
vernut'sia
Ispol'zuite ob'iasniaiushchie peremennye
Plokho:
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]);
Khorosho:
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
vernut'sia
Ispol'zuite ochelovechennye nazvaniia
Iavnoe luchshe, chem neiavnoe.
Plokho:
locations.forEach((l) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Chto znachit `l`?
dispatch(l);
});
Khorosho:
locations.forEach((location) => {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch(location);
});
vernut'sia
Ne dobavliaite nenuzhnyi kontekst
Esli vashe imia klassa / ob'ekta govorit vam chto eto, ne povtoriaite tozhe pri imenovanii ego svoistv i metodov.
Plokho:
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue'
};
function paintCar(car) {
car.carColor = 'Red';
}
Khorosho:
make: 'Honda',
model: 'Accord',
color: 'Blue'
};
function paintCar(car) {
car.color = 'Red';
}
vernut'sia
Ispol'zuite usloviia po umolchaniiu vmesto korotkikh zamykanii ili uslovnykh vyrazhenii
Plokho:
const breweryName = name || 'Hipster Brew Co.';
// ...
}
Khorosho:
// ...
}
vernut'sia
Funktsii
Argumenty funktsii (ideal'no 2 ili menee)
Ogranichenie kolichestva parametrov funktsii neveroiatno vazhno, poskol'ku ono uproshchaet testirovanie funktsii. Nalichie bolee chem triokh argumentov privodit k kombinatornomu vzryvu, kogda vam prikhoditsia perebirat' massu razlichnykh sluchaev s kazhdym otdel'nym argumentom.
Ideal'naia situatsiia -- otsutstvie argumentov. Odin ili dva argumenta -- khorosho, a trekh uzhe sleduet izbegat'.
Bol'shee kolichestvo argumentov neobkhodimo konsolidirovat'. Kak pravilo, esli peredaetsia bolee dvukh argumentov, vasha funktsiia pytaetsia sdelat' slishkom mnogoe. V tekh sluchaiakh, kogda eto vse zhe ne tak, luchshe ispol'zovat' ob'ekt v kachestve argumenta. Poskol'ku JavaScript pozvoliaet sozdavat' ob'ekty na letu, bez spetsial'nogo opisaniia klassov, ikh vpolne mozhno primeniat', kogda trebuetsia peredat' mnozhestvo argumentov.
Dlia togo, chtoby sdelat' svoistva funktsii ochevidnymi ispol'zuite sintaksis ES6 destruktsii. Eto imeet neskol'ko preimushchestv:
- Kogda prosmatrivaiut ob'iavlenie funktsii, to srazu poniatno, kakie svoistva ispol'zuiutsia.
- Destruktsiia takzhe kloniruet ukazannye prostye znacheniia iz argumenta funktsii. Eto mozhet pomoch' predotvratit' said effekty. Zametka: Ob'ekty i massivy NE KLONIRUIuTSIa.
- Lintery mogut predupredit' vas o neispol'zuemykh svoistvakh, chto bylo by nevozmozhno bez destrukturizatsii.
Plokho:
// ...
}
Khorosho:
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
vernut'sia
Funktsiia dolzhna reshat' odnu zadachu
Eto, bezuslovno, samoe vazhnoe pravilo v razrabotke programmnogo obespecheniia. Kogda funktsii reshaiut bolee odnoi zadachi, ikh trudnee sochetat', testirovat' i ponimat'. Kak tol'ko vy smozhete svesti kazhduiu funktsiiu k vypolneniiu tol'ko odnogo deistviia, ikh stanet znachitel'no proshche refaktorit', a vash kod stanet gorazdo bolee chitaemym. Dazhe esli privedennoe pravilo budet edinstvennym vynesennym vami iz etogo rukovodstva, vy vse ravno budete kruche mnogikh razrabotchikov.
Plokho:
clients.forEach((client) => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
Khorosho:
clients
.filter(isClientActive)
.forEach(email);
}
function isClientActive(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
vernut'sia
Nazvaniia funktsii dolzhny opisyvat' ikh naznachenie
Plokho:
// ...
}
const date = new Date();
// It's hard to to tell from the function name what is added
addToDate(date, 1);
Khorosho:
// ...
}
const date = new Date();
addMonthToDate(1, date);
vernut'sia
Funktsii dolzhny predstavliat' tol'ko odin uroven' abstraktsii
Esli v funktsii predstavleno bolee odnogo urovnia abstraktsii, to ona, kak pravilo, delaet slishkom mnogoe. Razdelenie takikh funktsii privedet k vozmozhnosti povtornogo ispol'zovaniia i oblegcheniiu testirovaniia.
Plokho:
const REGEXES = [
// ...
];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
// ...
});
});
const ast = [];
tokens.forEach((token) => {
// pravilo...
});
ast.forEach((node) => {
// parsing...
});
}
Khorosho:
const REGEXES = [
// ...
];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
return tokens;
}
function lexer(tokens) {
const ast = [];
tokens.forEach((token) => {
ast.push( /* ... */ );
});
return ast;
}
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const ast = lexer(tokens);
ast.forEach((node) => {
// parsing...
});
}
vernut'sia
Izbavliaites' ot dublirovannogo koda
Izo vsekh sil staraites' izbegat' dublirovannogo koda. Dublirovannyi kod vreden tem, chto podrazumevaet nalichie bolee chem odnogo mesta, v kotoroe pridetsia vnosit' pravki, esli logika deistvii izmenitsia.
Predstav'te, chto upravliaete restoranom i vedete uchet vsekh produktov -- pomidorov, luka, chesnoka, spetsii i t.d. Esli ikh uchet vedetsia v raznykh spiskakh, to podacha liubogo bliuda s pomidorami potrebuet vneseniia izmenenii v kazhdyi spisok. Esli zhe spisok tol'ko odin, to i pravka budet vsego odna!
Zachastuiu dublirovannyi kod voznikaet v tekh sluchaiakh, kogda trebuetsia realizovat' dva ili bolee neznachitel'no razlichaiushchikhsia deistviia, kotorye v tselom ochen' skhozhi, no ikh razlichiia vynuzhdaiut vas zavesti dve ili bolee funktsii, delaiushchikh prakticheski odno i to zhe. V etom sluchae izbavlenie ot dublirovannogo koda budet oznachat' sozdanie abstraktsii, kotoraia smozhet predstavit' vse razlichiia v vide odnoi funktsii, klassa ili modulia.
Sozdanie pravil'noi abstraktsii -- vopros neveroiatnoi vazhnosti, i imenno poetomu vy dolzhny sledovat' printsipam SOLID. Plokhie abstraktsii mogut okazat'sia khuzhe dublirovannogo koda, tak chto bud'te ostorozhny!
Podvodia itog: esli mozhete obernut' kod khoroshei abstraktsiei -- tak i sdelaite! Ne dubliruite kod, inache vam pridetsia vnosit' mnozhestvo pravok na kazhdoe nebol'shoe izmenenie.
Plokho:
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);
});
}
Khorosho:
employees.forEach((employee) => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
let portfolio = employee.getGithubLink();
if (employee.type === 'manager') {
portfolio = employee.getMBAProjects();
}
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
vernut'sia
Ustanavlivaite ob'ekty po umolchaniiu s pomoshch'iu Object.assign
Plokho:
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);
Khorosho:
title: 'Order',
// User did not include 'body' key
buttonText: 'Send',
cancellable: true
};
function createMenu(config) {
config = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// teper' config = {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig);
vernut'sia
Ne ispol'zuite flagi v kachestve parametrov funktsii
Flagi govoriat pol'zovateliu, chto funktsiia sovershaet bolee odnogo deistviia. Funktsiia dolzhna reshat' odnu zadachu. Razdeliaite funktsii, esli oni ispolniaiut razlichnye varianty koda na osnove logicheskogo znacheniia.
Plokho:
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
Khorosho:
fs.create(name);
}
function createTempFile(name) {
createFile(`./temp/${name}`);
}
vernut'sia
Izbegaite pobochnykh effektov (Chast' 1)
Funktsiia proizvodit pobochnyi effekt, esli ona sovershaet kakoe-libo deistvie pomimo polucheniia znacheniia i vozvrata drugogo znacheniia ili znachenii. Pobochnyi effekt mozhet byt' zapis'iu v fail, izmeneniem kakikh-to global'nykh peremennykh ili sluchainym perevodom vsekh vashikh deneg neizvestnym litsam.
Vprochem, pobochnye effekty v programme neobkhodimy. Pust', kak i v predydushchem primere, vam trebuetsia zapis' v fail. Opishite to, chto vy khotite sdelat', strogo v odnom meste.
Ne sozdavaite neskol'ko funktsii i klassov, kotorye pishut chto-to v konkretnyi fail. Sozdaite odin servis, kotoryi vsem etim zanimaetsia. Odin i tol'ko odin.
Sut' v tom, chtoby izbegat' rasprostranennykh oshibok, takikh kak, naprimer, peredacha sostoianiia mezhdu ob'ektami bez kakoi-libo struktury, s pomoshch'iu izmeniaemykh dannykh, kotorye mozhet perezapisyvat' kto ugodno, v obkhod tsentralizovannogo mesta primeneniia pobochnykh effektov.
Esli nauchites' tak delat', vy stanete schastlivee, chem podavliaiushchee bol'shinstvo drugikh programmistov.
Plokho:
// Esli by u nas byla eshche odna funktsiia, kotoraia by rabotala s imenem name kak so strokoi,
// to obnaruzhiv massiv, on nepremenno by polomalas'
let name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
name = name.split(' ');
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];
Khorosho:
return name.split(' ');
}
const name = 'Ryan McDermott';
const newName = splitIntoFirstAndLastName(name);
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
vernut'sia
Izbegaite pobochnykh effektov (Chast' 2)
V JavaScript primitivy peredaiutsia po znacheniiu, a ob'ekty i massivy peredaiutsia po ssylke. V sluchae ob'ektov i massivov, esli nasha funktsiia vnosit izmeneniia v korzinu (massiv), naprimer, putem dobavleniia elementa v massiv, to liubaia drugaia funktsiia, kotoraia ispol'zuet etu korzinu (massiv) budet zaviset' ot etogo dobavleniia. Eto mozhet byt' i khorosho i plokho v raznykh sluchaiakh. Davaite predstavim sebe plokhuiu situatsiia:
Pol'zovatel' nazhimaet na knopku "Pokupka", kotoraia vyzyvaet funktsiiu purchase, chto otpravliaet dannye iz korziny (massiv) na server. V sluchae plokhogo podkliucheniia k seti funktsiia purchase dolzhna otpravit' povtornyi zapros. Teper', chto, esli v to zhe vremia pol'zovatel' sluchaino nazhimaet knopku "Dobavit' v korzinu", no poka ne khochet pokupat' tovar?
Esli eto proizoidet, i nachinaetsia zapros seti, to funktsiia purchase
poshlet sluchaino dobavlennyi element, poskol'ku on imeet ssylku na predydushchuiu korzinu (massiv), modifitsirovannuiu funktsiei addItemToCart. Otlichnoe reshenie bylo by dlia addItemToCart vsegda klonirovat' korzinu, otredaktirovat' i vernut' klon. Eto garantiruet, chto nikakie drugie funktsii, kotorye zavisiat ot korziny ne budut zaviset' ot kakikh-libo izmenenii.
Dva predosterezheniia po-povodu takogo podkhoda:
- Vozmozhny sluchai, kogda vy na samom dele khotite izmenit' ob'ekt po ssylke, no takie sluchai kraine redki. Bol'shinstvo funktsii mogut byt' ob'iavleny bez said effektov!
- Klonirovanie bol'shikh ob'ektov mozhet byt' ochen' nagruzochnym i vliiat' na proizvoditel'nost'. K schast'iu, eto ne iavliaetsia bol'shoi problemoi na praktike, potomu chto est' otlichnye biblioteki, kotorye pozvoliaiut klonirovat' ob'ekty s men'shei nagruzkoi na pamiat' v otlichii ot klonirovaniia vruchnuiu.
Plokho:
cart.push({ item, date: Date.now() });
};
Khoroshe:
return [...cart, { item, date : Date.now() }];
};
vernut'sia
Ne pereopredeliaite global'nye funktsii
Zagriaznenie global'nykh peremennykh -- plokhaia praktika v JavaScript, tak kak mozhet porodit' konflikty s drugoi bibliotekoi, i pol'zovatel' vashego API ne uvidit oshibok, poka ne poluchit iskliuchenie v prodakshene. Davaite rassmotrim primer: chto delat', esli vy khotite rasshirit' standartnyi funktsional Array iz JavaScript, dobaviv metod diff, kotoryi by vychislial razlichie mezhdu dvumia massivami? Vy dolzhny byli by zapisat' novuiu funktsiiu v Array.prototype, no togda ona mozhet voiti v konflikt s drugoi bibliotekoi, kotoraia pytalas' sdelat' to zhe samoe. A esli drugaia biblioteka ispol'zovala metod diff, chtoby naiti raznitsu mezhdu pervym i poslednim elementami massiva? Imenno poetomu gorazdo luchshe ispol'zovat' klassy ES2015/ES6 i prosto unasledovat' nashu realizatsiiu ot klassa Array.
Plokho:
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
Khorosho:
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
vernut'sia
Otdavaite predpochtenie funktsional'nomu programmirovaniiu nad imperativnym
JavaScript ne nastol'ko funktsional'nyi iazyk, kak Haskell, no opredelennoi doli funktsional'nosti on ne lishen. Funktsional'nye iazyki chishche i ikh proshche testirovat'. Primeniaite funktsional'nyi stil' programmirovaniia pri vozmozhnosti.
Plokho:
{
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;
}
Khorosho:
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
const INITIAL_VALUE = 0;
const totalOutput = programmerOutput
.map((programmer) => programmer.linesOfCode)
.reduce((acc, linesOfCode) => acc + linesOfCode, INITIAL_VALUE);
vernut'sia
Inkapsuliruite usloviia
Plokho:
// ...
}
Khorosho:
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
}
vernut'sia
Izbegaite negativnykh uslovii
Plokho:
// ...
}
if (!isDOMNodeNotPresent(node)) {
// ...
}
Khorosho:
// ...
}
if (isDOMNodePresent(node)) {
// ...
}
vernut'sia
Izbegaite uslovnykh konstruktsii
Takaia zadacha kazhetsia nevozmozhnoi. Uslyshav podobnoe, bol'shinstvo liudei govoriat: "Kak ia dolzhen delat' chto-libo bez vyrazheniia if?". Otvet zakliuchaetsia v tom, chto vo mnogikh sluchaiakh dlia dostizheniia tekh zhe tselei mozhno ispol'zovat' polimorfizm. Vtoroi vopros, kak pravilo, zvuchit tak: "Khorosho, zamechatel'no, no pochemu ia dolzhen ikh izbegat'?". Otvet -- predydushchaia kontseptsiia chistogo koda, kotoruiu my uznali: funktsiia dolzhna vypolniat' tol'ko odnu zadachu. Esli u vas est' klassy i funktsii, soderzhashchie konstruktsiiu if, vy slovno govorite svoemu pol'zovateliu, chto vasha funktsiia vypolniaet bol'she odnoi zadachi. Pomnite: odna funktsiia -- odna zadacha.
Plokho:
// ...
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}
Khorosho:
// ...
}
class Boeing777 extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
class AirForceOne extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
class Cessna extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
vernut'sia
Izbegaite proverki tipov (chast' 1)
JavaScript iavliaetsia netipizirovannym iazykom, a eto znachit, chto vashi funktsii mogut prinimat' argumenty liubogo tipa. Poroi vy obzhigalis' etoi svobodoi, chto pobuzhdalo vas proizvodit' proverku tipov v vashikh funktsiiakh. Est' mnozhestvo sposobov ee izbezhat'. V pervuiu ochered' stoit podumat' nad soglasovannym API.
Plokho:
if (vehicle instanceof Bicycle) {
vehicle.peddle(this.currentLocation, new Location('texas'));
} else if (vehicle instanceof Car) {
vehicle.drive(this.currentLocation, new Location('texas'));
}
}
Khorosho:
vehicle.move(this.currentLocation, new Location('texas'));
}
vernut'sia
Izbegaite proverki tipov (chast' 2)
Esli vy rabotaete s bazovymi primitivami, takimi kak stroki, tselye chisla i massivy, i ne mozhete ispol'zovat' polimorfizm, khotia vse eshche chuvstvuete neobkhodimost' v proverkakh tipa, vam stoit rassmotret' vozmozhnost' primeneniia TypeScript. Eto otlichnaia al'ternativa obychnomu JavaScript, predostavliaiushchaia vozmozhnost' staticheskoi tipizatsii poverkh standartnogo sintaksisa JavaScript. Problema s ruchnoi proverkoi tipov v obychnom JavaScript v tom, chto illiuziia bezopasnosti, kotoruiu ona sozdaet, nikak ne kompensiruetsia poterei chitabel'nosti iz-za mnogoslovnosti koda. Derzhite vash kod v chistote, pishite khoroshie testy i delaite effektivnye revizii koda. Ili delaite vse to zhe samoe, no s pomoshch'iu TypeScript (kotoryi, kak ia uzhe skazal, iavliaetsia prekrasnoi al'ternativoi!).
Plokho:
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');
}
Khorosho:
return val1 + val2;
}
vernut'sia
Ne optimiziruite sverkh mery
Sovremennye brauzery proizvodiat mnozhestvo optimizatsii pod kapotom vo vremia ispolneniia koda. Optimiziruia kod vruchnuiu, vy, zachastuiu, prosto tratite svoe vremia. Est' prekrasnye resursy s opisaniem situatsii, kogda optimizatsiia deistvitel'no khromaet. Pogliadyvaite na nikh v svobodnoe vremia, poka eti problemy ne budut ispravleny, esli voobshche budut, konechno.
Plokho:
// because of `list.length` recomputation. In modern browsers, this is optimized.
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
Khorosho:
// ...
}
vernut'sia
Udaliaite mertvyi kod
Mertvyi kod tak zhe plokh, kak povtoriaiushchiisia kod. Net nikakikh prichin, chtoby derzhat' ego v repozitorii. Esli kod ne vyzyvaetsia, izbav'tes' ot nego!
On po-prezhnemu budet v sisteme kontrolia versii, esli kogda-nibud' on vse-taki vam ponadobitsia.
Plokho:
// ...
}
function newRequestModule(url) {
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
Khorosho:
// ...
}
const req = newRequestModule;
inventoryTracker('apples', req, 'www.inventory-awesome.io');
vernut'sia
Ob'ekty i struktury dannykh
Ispol'zuite gettery i settery
V javascript otsutstvuiut kliuchevye slova private i public, chto uslozhniaet realizatsiiu klassov. Luchshe ispol'zovat' gettery i settery dlia dostupa k svoistvam ob'ekta, chem napriamuiu k nim obrashchat'sia. Vy sprosite <
- Esli vy khotite realizovat' bol'she, chem prosto dostup k svoistvu, vam nuzhno pomeniat' realizatsiiu v odnom meste, a ne po vsemu kodu.
- Validatsiiu legko realizovat' na urovne realizatsii settera
- Inkapsuliatsiia vnutrennego sostoianiia ob'ekta
- Legko dobavit' logirovanie i obrabotku oshibok na urovne getterov i setterov
- Nasleduia etot klass, vy mozhete pereopredelit' funktsional'nost' po umolchaniiu
- Vy mozhete lenivo podgruzhat' svoistva vashego ob'ekta, naprimer, s servera.
Plokho:
constructor() {
this.balance = 1000;
}
}
const bankAccount = new BankAccount();
// Pokupaem, naprimer, obuv'...
bankAccount.balance -= 100;
Khorosho:
constructor(balance = 1000) {
this._balance = balance;
}
set balance(amount) {
if (this.verifyIfAmountCanBeSetted(amount)) {
this._balance = amount;
}
}
get balance() {
return this._balance;
}
verifyIfAmountCanBeSetted(val) {
// ...
}
}
const bankAccount = new BankAccount();
// Pokupaem, naprimer, obuv'...
bankAccount.balance -= shoesPrice;
// poluchaem balans
let balance = bankAccount.balance;
vernut'sia
Realizuite privatnye svoistva vashikh ob'ektov
Eto vozmozhno s pomoshch'iu zamykanii.
Plokho:
this.name = name;
};
Employee.prototype.getName = function getName() {
return this.name;
};
const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
Khorosho:
this.getName = function getName() {
return name;
};
};
const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
vernut'sia
Klassy
Printsip edinstvennoi otvetstvennosti (SRP)
Kak napisano v clean code, <
Plokho:
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
Khorosho:
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class UserSettings {
constructor(user) {
this.user = user;
this.auth = new UserAuth(user);
}
changeSettings(settings) {
if (this.auth.verifyCredentials()) {
// ...
}
}
}
vernut'sia
Printsip otkrytosti/zakrytosti (OCP)
Kak zaiavil Bertran Meier, <
Plokho:
constructor() {
super();
this.name = 'ajaxAdapter';
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = 'nodeAdapter';
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
if (this.adapter.name === 'ajaxAdapter') {
return makeAjaxCall(url).then((response) => {
// transform response and return
});
} else if (this.adapter.name === 'httpNodeAdapter') {
return makeHttpCall(url).then((response) => {
// transform response and return
});
}
}
}
function makeAjaxCall(url) {
// request and return promise
}
function makeHttpCall(url) {
// request and return promise
}
Khorosho:
constructor() {
super();
this.name = 'ajaxAdapter';
}
request(url) {
// request and return promise
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = 'nodeAdapter';
}
request(url) {
// request and return promise
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
return this.adapter.request(url).then((response) => {
// transform response and return
});
}
}
vernut'sia
Printsip podstanovki Barbary Liskov
Eto strashnyi termin dlia ochen' prostoi kontseptsii.
Opredelenie:
<
Sut' zakliuchaetsia v tom, chto esli u vas est' roditel'skii i dochernii klassy, to oni mogut vzaimozameniat'sia bez oshibok. Eto po-prezhnemu mozhet sbivat' s tolku, tak chto davaite posmotrim na klassicheskii primer ploshchadi priamougol'nika. Matematicheski kvadrat eto priamougol'nik, no esli vy reshite etu zadachu s pomoshch'iu nasledovaniia, to u vas budut problemy. Bolee detal'no pro printsip mozhno pochitat' zdes'.
Plokho:
constructor() {
this.width = 0;
this.height = 0;
}
setColor(color) {
// ...
}
render(area) {
// ...
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
function renderLargeRectangles(rectangles) {
rectangles.forEach((rectangle) => {
rectangle.setWidth(4);
rectangle.setHeight(5);
const area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20.
rectangle.render(area);
});
}
const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);
Khorosho:
setColor(color) {
// ...
}
render(area) {
// ...
}
}
class Rectangle extends Shape {
constructor() {
super();
this.width = 0;
this.height = 0;
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Shape {
constructor() {
super();
this.length = 0;
}
setLength(length) {
this.length = length;
}
getArea() {
return this.length * this.length;
}
}
function renderLargeShapes(shapes) {
shapes.forEach((shape) => {
switch (shape.constructor.name) {
case 'Square':
shape.setLength(5);
break;
case 'Rectangle':
shape.setWidth(4);
shape.setHeight(5);
}
const area = shape.getArea();
shape.render(area);
});
}
const shapes = [new Rectangle(), new Rectangle(), new Square()];
renderLargeShapes(shapes);
vernut'sia
Printsip razdeleniia interfeisa (ISP)
V javascript otsutstvuiut interfeisy, tak chto etot printsip ne poluchitsia ispol'zovat' v polnoi mere. Tem ne menee vazhno ego ispol'zovat', dazhe pri otsutstvii sistemy tipov javascript.
ISP utverzhdaet, chto <
Plokho:
constructor(settings) {
this.settings = settings;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.animationModule.setup();
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName('body'),
animationModule() {} // Chashche vam ne nuzhna animatsiia pri dvizhenii.
// ...
});
Khorosho:
constructor(settings) {
this.settings = settings;
this.options = settings.options;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.setupOptions();
}
setupOptions() {
if (this.options.animationModule) {
// ...
}
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName('body'),
options: {
animationModule() {}
}
});
vernut'sia
Printsip inversii zavisimosti (DIP)
Etot printsip glasit dve vazhnye veshchi:
- Moduli vysshego urovnia ne dolzhny zaviset' ot modulei nizshego urovnia. Oba dolzhny zaviset' ot abstraktsii.
- V abstraktsiiakh ne dolzhno byt' detalei. Detali dolzhny byt' v dochernikh klassakh.
Snachala trudno poniat' etot printsip. No esli vy rabotali s AngularJS, vy videli realizatsiiu etogo printsipa v vide Dependency Injection (DI). Nesmotria na to, chto oni ne iavliaiutsia identichnymi poniatiiami, DIP daiot vozmozhnost' otgranichit' moduli vysokogo urovnia ot detalei modulei nizkogo urovnia i ustanovki ikh. On mozhet sdelat' eto cherez DI. Etot printsip umen'shaet sviaz' mezhdu moduliami. Esli vashi moduli tesno sviazany, ikh tiazhelo refaktorit'.
Abstraktsii i est' neiavnymi soglasheniiami, kotorye predstavliaiut interfeisy v JavaScript. To est' metody i svoistva, chto ob'ekt/klass predostavliaet drugomu ob'ektu/klassu. V privedennom nizhe primere kazhdyi ekzempliar klassa InventoryTracker budet imet' metod requestItems.
Plokho:
constructor() {
this.REQ_METHODS = ['HTTP'];
}
requestItem(item) {
// ...
}
}
class InventoryTracker {
constructor(items) {
this.items = items;
// Plokho to, chto my sozdali zavisimost' ot konkretnoi realizatsii zaprosa.
// teper' nash metod requestItems ne abstraktnyi i zavisit ot etoi realizatsii
this.requester = new InventoryRequester();
}
requestItems() {
this.items.forEach((item) => {
this.requester.requestItem(item);
});
}
}
const inventoryTracker = new InventoryTracker(['apples', 'bananas']);
inventoryTracker.requestItems();
Khorosho:
constructor(items, requester) {
this.items = items;
this.requester = requester;
}
requestItems() {
this.items.forEach((item) => {
this.requester.requestItem(item);
});
}
}
class InventoryRequesterV1 {
constructor() {
this.REQ_METHODS = ['HTTP'];
}
requestItem(item) {
// ...
}
}
class InventoryRequesterV2 {
constructor() {
this.REQ_METHODS = ['WS'];
}
requestItem(item) {
// ...
}
}
// Sformirovav zavisimosti izvne, my mozhem legko
// zamenit' nash modul' zaprosov na drugoi, kotoryi ispol'zuet vebsokety
const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterV2());
inventoryTracker.requestItems();
vernut'sia
Otdavaite predpochtenie klassam (ES2015 / ES6) nad prostymi funktsiiami (ES5)
C pomoshch'iu klassicheskikh (ES5) klassov tiazhelo realizovat' chitaemye nasledovanie, konstruktsiiu i opredelenie metodov. Esli vam nuzhno nasledovanie, ne zadumyvaias' ispol'zuite (ES2015 / ES6) klassy. Tem ne menee, otdavaite predpochtenie malen'kim funktsiiam, a ne klassam, poka ne budet neobkhodimosti v bolee krupnykh i slozhnykh ob'ektakh.
Plokho:
if (!(this instanceof Animal)) {
throw new Error('Instantiate Animal with `new`');
}
this.age = age;
};
Animal.prototype.move = function move() {};
const Mammal = function(age, furColor) {
if (!(this instanceof Mammal)) {
throw new Error('Instantiate Mammal with `new`');
}
Animal.call(this, age);
this.furColor = furColor;
};
Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};
const Human = function(age, furColor, languageSpoken) {
if (!(this instanceof Human)) {
throw new Error('Instantiate Human with `new`');
}
Mammal.call(this, age, furColor);
this.languageSpoken = languageSpoken;
};
Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};
Khorosho:
constructor(age) {
this.age = age;
}
move() { /* ... */ }
}
class Mammal extends Animal {
constructor(age, furColor) {
super(age);
this.furColor = furColor;
}
liveBirth() { /* ... */ }
}
class Human extends Mammal {
constructor(age, furColor, languageSpoken) {
super(age, furColor);
this.languageSpoken = languageSpoken;
}
speak() { /* ... */ }
}
vernut'sia
Ispol'zuite metod tsepochki
Etot pattern ochen' polezen v JavaScript. Ego ispol'zuiut mnogie biblioteki, takie kak jQuery i Lodash. Eto delaet vash kod vyrazitel'nym i nemnogoslovnym. Ispol'zuia etot pattern, vy uvidite naskol'ko vash kod stanet chishche. Prosto vozvrashchaite this, v kontse vashikh metodov i vy smozhete vyzyvat' ikh po tsepochke.
Plokho:
constructor() {
this.make = 'Honda';
this.model = 'Accord';
this.color = 'white';
}
setMake(make) {
this.make = make;
}
setModel(model) {
this.model = model;
}
setColor(color) {
this.color = color;
}
save() {
console.log(this.make, this.model, this.color);
}
}
const car = new Car();
car.setColor('pink');
car.setMake('Ford');
car.setModel('F-150');
car.save();
Khorosho:
constructor() {
this.make = 'Honda';
this.model = 'Accord';
this.color = 'white';
}
setMake(make) {
this.make = make;
// vozvrashchaem this dlia vyzova po tsepochke
return this;
}
setModel(model) {
this.model = model;
// vozvrashchaem this dlia vyzova po tsepochke
return this;
}
setColor(color) {
this.color = color;
// vozvrashchaem this dlia vyzova po tsepochke
return this;
}
save() {
console.log(this.make, this.model, this.color);
// vozvrashchaem this dlia vyzova po tsepochke
return this;
}
}
const car = new Car()
.setColor('pink')
.setMake('Ford')
.setModel('F-150')
.save();
vernut'sia
Otdavaite predpochtenie kompozitsii nad nasledovaniem
Kak bylo skazano v knige Design Patterns ot Bandy chetyrekh, sleduet otdavat' predpochtenie kompozitsii nad nasledovaniem, gde vy tol'ko mozhete. Est' mnogo prichin, chtoby ispol'zovat' nasledovanie i mnogo prichin ispol'zovat' kompozitsiiu. Esli vash mozg instinktivno vidit nasledovanie, poprobuite predstavit' reshenie vashei problemy s pomoshch'iu kompozitsii.
Kogda zhe ispol'zovat' nasledovanie? Eto zavisit ot konkretnoi problemy. Vot spisok sluchaev, kogda nasledovanie imeet bol'she smysla, chem kompozitsiia:
- Kogda nasledovanie predstavliaet soboi zavisimost' <
>, a ne < > (Human->Animal vs. User->UserDetails) - Vy mozhete povtorno ispol'zovat' klass (Liudi mogut dvigat'sia kak i vse zhivotnye).
- Vy khotite, sdelav izmeneniia roditel'skogo klassa, izmenit' dochernie klassy (Izmenenie raskhoda kalorii vsekh zhivotnykh, kogda oni dvigaiutsia).
Plokho:
constructor(name, email) {
this.name = name;
this.email = email;
}
// ...
}
// U sotrudnikov est' nalogovye dannye. Nalogovye dannye ne mogut byt' sotrudnikom.
class EmployeeTaxData extends Employee {
constructor(ssn, salary) {
super();
this.ssn = ssn;
this.salary = salary;
}
// ...
}
Khorosho:
constructor(ssn, salary) {
this.ssn = ssn;
this.salary = salary;
}
// ...
}
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
setTaxData(ssn, salary) {
this.taxData = new EmployeeTaxData(ssn, salary);
}
// ...
}
vernut'sia
Testirovanie
Testirovanie ochen' vazhnaia chast' razrabotki. Esli u vas net testov ili ikh nedostatochno, kak vy mozhete byt' uvereny, chto vy nichego ne slomaete? Vasha komanda dolzhna sama prinimat' reshenie po ob'iomu koda pokrytogo testami, no chem bol'she pokryto testami, tem spokoinee spit razrabotchik. Eto oznachaet, chto v dopolnenie k nalichiiu khoroshego instrumenta dlia testirovaniia, neobkhodimo takzhe ispol'zovat' khoroshii instrument dlia proverki pokrytiia koda testami. Net prichin ne pisat' testy. Vot podborka khoroshikh instrumentov dlia testirovaniia. Podobrav udobnyi dlia vashei komandy, pishite testy dlia kazhdogo novogo modulia ili fichi. Esli vy vybrali razrabotku cherez testirovanie (Test Driven Development TDD) - eto prekrasno, no glavnoe zakliuchaetsia v tom, chtoby ubedit'sia, chto testy pokryvaiut vse vashi tseli pered razrabotkoi novogo koda, ili refaktoringom sushchestvuiushchego koda.
Odin test - odno opisanie.
Plokho:
describe('MakeMomentJSGreatAgain', () => {
it('handles date boundaries', () => {
let date;
date = new MakeMomentJSGreatAgain('1/1/2015');
date.addDays(30);
date.shouldEqual('1/31/2015');
date = new MakeMomentJSGreatAgain('2/1/2016');
date.addDays(28);
assert.equal('02/29/2016', date);
date = new MakeMomentJSGreatAgain('2/1/2015');
date.addDays(28);
assert.equal('03/01/2015', date);
});
});
Khorosho:
describe('MakeMomentJSGreatAgain', () => {
it('handles 30-day months', () => {
const date = new MakeMomentJSGreatAgain('1/1/2015');
date.addDays(30);
date.shouldEqual('1/31/2015');
});
it('handles leap year', () => {
const date = new MakeMomentJSGreatAgain('2/1/2016');
date.addDays(28);
assert.equal('02/29/2016', date);
});
it('handles non-leap year', () => {
const date = new MakeMomentJSGreatAgain('2/1/2015');
date.addDays(28);
assert.equal('03/01/2015', date);
});
});
vernut'sia
Asinkhronnost'
Ispol'zuite promisy vmesto kolbekov
Kolbeki privodiat k chrezmernoi vlozhennosti i plokhoi chitaemosti koda.
Plokho
const fs = require('fs');
const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';
request.get(url, (requestErr, response) => {
if (requestErr) {
console.error(requestErr);
} else {
fs.writeFile('article.html', response.body, (writeErr) => {
if (writeErr) {
console.error(writeErr);
} else {
console.log('File written');
}
});
}
});
Khorosho
const fsPromise = require('fs-promise');
const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';
requestPromise.get(url)
.then((response) => {
return fsPromise.writeFile('article.html', response);
})
.then(() => {
console.log('File written');
})
.catch((err) => {
console.error(err);
});
vernut'sia
Async/Await delaet kod chishche, chem promisy
Promisy ochen' khoroshaia al'ternativa kolbekam, no v ES2017 / ES8 spetsifikatsii poiavilsia async/await, kotoryi predlagaet eshchio luchshee reshenie. Vse, chto vam nuzhno, eto napisat' funktsiiu s prefiksom async, vnutri kotoroi vy mozhete pisat' vashu asinkhronnuiu logiku imperativno. async/await mozhno ispol'zovat' priamo seichas pri pomoshchi babel.
Plokho
const fsPromise = require('fs-promise');
const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';
requestPromise.get(url)
.then((response) => {
return fsPromise.writeFile('article.html', response);
})
.then(() => {
console.log('File written');
})
.catch((err) => {
console.error(err);
});
Khorosho
const fsPromise = require('fs-promise');
async function getCleanCodeArticle() {
try {
const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';
const response = await requestPromise.get(url);
await fsPromise.writeFile('article.html', response);
console.log('File written');
} catch(err) {
console.error(err);
}
}
vernut'sia
Obrabotka oshibok
Brosat' oshibki -- khoroshee reshenie! Eto oznachaet, chto vo vremia vypolneniia vy budete znat', esli chto-to poshlo ne tak. Vy smozhete ostanovit' vypolnenie vashego prilozheniia v nuzhnyi moment i videt' mesto oshibki s pomoshch'iu stek treisa v konsoli.
Ne ignoriruite otlovlennye oshibki
Nichego ne delaia s poimannoi oshibkoi, vy teriaete vozmozhnost' ispravit' oshibku ili otreagirovat' na neio kogda-libo. Vyvod oshibki v konsol'(console.log(error)) ne daet luchshego rezul'tata, potomu chto oshibka mozhet poteriat'sia sredi vyvodimykh zapisei v konsol'. Esli vy zavorachivaete kusok koda v try / catch, znachit vy predpolagaete vozniknovenie oshibki. V takom sluchae vy dolzhny imet' zapasnoi plan.
Plokho
functionThatMightThrow();
} catch (error) {
console.log(error);
}
Khorosho
functionThatMightThrow();
} catch (error) {
// Odin iz variantov (bolee zametnyi, chem console.log):
console.error(error);
// Drugoi variant - izvestit' pol'zovatelia pro oshibku:
notifyUserOfError(error);
// I eshche variant - otpravit' oshibku na server :
reportErrorToService(error);
// Ili ispol'zuite vse tri varianta!
}
vernut'sia
Ne ignoriruite oshibki, voznikshie v promisakh
Vy ne dolzhny ignorirovat' oshibki, voznikshie v promise, po toi zhe prichine, chto otlovlennye oshibki v try / catch.
Plokho
.then((data) => {
functionThatMightThrow(data);
})
.catch((error) => {
console.log(error);
});
Khorosho
.then((data) => {
functionThatMightThrow(data);
})
.catch((error) => {
// Odin iz variantov (bolee zametnyi, chem console.log):
console.error(error);
// Drugoi variant - izvestit' pol'zovatelia pro oshibku:
notifyUserOfError(error);
// I eshche variant - otpravit' oshibku na server :
reportErrorToService(error);
// Ili ispol'zuite vse tri varianta!
});
vernut'sia
Formatirovanie
Formatirovanie nosit sub'ektivnyi kharakter. Net zhestkikh pravil, kotorye vy dolzhny sobliudat'. Glavnoe pravilo -- ne sporit' po povodu formatirovaniia. Est' kucha instrumentov dlia avtomatizatsii etogo. Ispol'zuite tol'ko odnu! Spor po povodu formatirovaniia -- eto pustaia trata vremeni i deneg dlia razrabotchikov.
Dlia veshchei, kotorye ne podpadaiut pod sferu deistviia avtomaticheskogo formatirovaniia (otstupy, tabuliatsiia ili probely, dvoinye ili odinarnye kavychki i t.d.) smotrite kakoe-to rukovodstvo.
Ispol'zuite odin variant imenovaniia
JavaScript iavliaetsia netipizirovannym, poetomu imenovanie vashikh peremennykh, funktsii i t.d govorit vam mnogo chego o nikh. Eti pravila nosiat sub'ektivnyi kharakter, tak chto vasha komanda mozhet vybrat' tot variant, kotoryi khochet. Nevazhno kakoi variant vy vyberite, glavnoe priderzhivaites' vashego vybora.
Plokho
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 {}
Khorosho
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 {}
vernut'sia
Sviazannye funktsii dolzhny nakhoditsia riadom
Esli funktsiia vyzyvaet druguiu, sokhranite eti funktsii vertikal'no blizko v iskhodnom faile. V ideale, funktsiia, kotoraia ispol'zuet druguiu funktsiiu, dolzhna byt' priamo nad nei. My sklonny chitat' kod sverkhu-vniz, kak gazetu. Iz-za etogo udobno razmeshchat' kod takim obrazom.
Plokho
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(user);
review.perfReview();
Khorosho
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();
vernut'sia
Kommentarii
Kommentiruite tol'ko tot kod, kotoryi opisyvaet slozhnuiu biznes-logiku
Kommentarii ne obiazatel'ny. Khoroshii kod opisyvaet sebia sam.
Plokho
// khesh
let hash = 0;
// dlina stroki
const length = data.length;
// Prokhod po kazhdomu simvolu dannykh
for (let i = 0; i < length; i++) {
// Beriom simvol.
const char = data.charCodeAt(i);
// Delaem khesh
hash = ((hash << 5) - hash) + char;
// Preobrazovuem v 32-bitnoe chislo
hash &= hash;
}
}
Khorosho
let hash = 0;
const length = data.length;
for (let i = 0; i < length; i++) {
const char = data.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
// Preobrazovuem v 32-bitnoe chislo
hash &= hash;
}
}
vernut'sia
Ne kommentiruite nenuzhnyi kod
Dlia etogo sushchestvuiut sistemy kontrolia versii. Ostav'te staryi kod v istorii sistemy kontrolia versii.
Plokho
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();
Khorosho
vernut'sia
Ne vedite zhurnal kommentariev
Pomnite: nuzhno ispol'zovat' sistemu kontrolia versii! Net neobkhodimosti v neispolniaemom kode, zakommentirovannom kode i osobenno v zhurnale kommentariev. Ispol'zuite git log, chtoby poluchit' istoriiu!
Plokho
* 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;
}
Khorosho
return a + b;
}
vernut'sia
Izbegaite pozitsionnykh markerov
Oni, kak pravilo, prosto meshaiut. Pust' funktsii i imena peremennykh vmeste s sootvetstvuiushchim uglubleniem i formatirovaniem daiut vizual'nuiu strukturu koda.
Plokho
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
menu: 'foo',
nav: 'bar'
};
////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
const actions = function() {
// ...
};
Khorosho
menu: 'foo',
nav: 'bar'
};
const actions = function() {
// ...
};
vernut'sia
Perevody
Takzhe dostupna na drugikh iazykakh:
- English: ryanmcdermott/clean-code-javascript
- Brazilian Portuguese: fesnt/clean-code-javascript
- Chinese: alivebao/clean-code-js
- German: marcbruederlin/clean-code-javascript
- Korean: qkraudghgh/clean-code-javascript-ko
vernut'sia