«Программы пишутся для чтения людьми и лишь изредка запускаются компьютерами». — Дональд Эрвин Кнут (Gartner)
О кодовой грамотности
Мы часто говорим о слове «грамотность», которое относится к своего рода достижениям, которые человек практиковал и тренировал в профессиональной сфере, и имеет разные проявления в разных областях, например, в области музыки «музыкальная грамотность». относится к индивидуальному чувству музыки, контролю высоты тона и ритма, способности ценить разные музыкальные жанры и т. д., а в области программирования также различаются грамотность, отражающая базовые навыки, чистоту кода, профессиональную отношение и т. д., так называемая «грамотность кода», говоря простым языком, относится к тому, элегантен ли код, красив и удобен в сопровождении.
Абсолютно идеального кода не существует, а грамотность кода не означает перфекционизма. В области перевода существует стандарт «верности, выразительности и элегантности», причина, по которой «изящность» стоит на последнем месте, заключается в том, что для ее достижения требуется относительно высокая планка и накопление опыта. По аналогии со сферой программирования, когда мы программируем, первое, о чем мы думаем, это как реализовать бизнес-логику, а не как написать код элегантно, поэтому в написании кода нет так называемой абсолютной элегантности. Однако в качествеПрофессиональный фронтенд-инженер, если быть точным, должно бытьПрофессиональный инженер-программист, Написание элегантного кода должно быть постоянным занятием, это больше похоже на критерий, так как все знают что делать и чего не делать, так называемый принцип, так называемый итоговый результат, отражает так называемый“代码素养”.
Теория разбитого окна
Теория разбитых окон, первоначальное значение относится к разбитым окнам, здание оставлено без присмотра, люди позволяют окнам продолжать разбиваться, и в конечном итоге они сами участвуют в диверсионной деятельности, граффити на наружных стенах, позволяют накапливаться мусору, и, наконец, пойти на крах.
Теория разбитого окна очень легко проявляется на практике, часто у первого человека код написан не очень хорошо, а у второго возникают мысли типа «Все равно он уже так написал, поэтому я могу сделать только это», что приводит к коду Чем больше обслуживания, тем сложнее, и он рушится в последний момент, превращая его в мусор, который никто не хочет поддерживать.
чистый код
Чистый код похож на красивую прозу.Представьте себе хорошую книгу, которую вы прочитали.В ней может подниматься и опускаться стиль письма автора.Он полон картинок и мобилизует ваши эмоции. В коде нет такой кульминации, но чистый код должен быть полон напряжения, которое можно использовать в какой-то момент, чтобы довести сюжет до кульминации.
Я предпочитаю сравнивать написание кода с написанием статей и рассказыванием историй. Написание кода — это творческий процесс. Автору нужно выразить в виде кода то, что он хочет выразить, а чистый код подобен рассказу истории. люди чувствуют себя невежественными и не понимают, о чем говорят.
принципы чистого кода
В современном интерфейсном развитии есть много инструментов автоматизации, чтобы помочь нам написать спецификации, такие какeslint,tslintи другие вспомогательные средства проверки, известные спецификации, такие какgoogle规范,airbnb规范Etc. также ограничен различными деталями, чтобы помочь нам сформировать разумный и стандартизированный стиль кода.
В этом разделе не будет повторяться стиль кода на уровне языка.В соответствии с фактическим проектом рефакторинга обобщается ряд принципов, на которые необходимо обратить внимание в процессе разработки, и они расположены в соответствии с приоритетом важности.
1. DRY(Don't Repeat Yourself)
Я полагаю, что как инженер-программист все слышали о самом основном принципе DRY.Многие шаблоны проектирования, включая собственно объектно-ориентированный, усердно работают по этому принципу.
DRY, как следует из названия, означает «не повторяйся», что на самом деле подчеркивает принцип абстракции: если один и тот же или похожий фрагмент кода появляется более двух раз, он должен быть абстрагирован в общий метод или файл, который должен быть используется в Местах, чтобы полагаться на введение, чтобы гарантировать, что при внесении изменений необходимо настроить только одно место, и все места будут изменены, вместо того, чтобы идти в каждое место, чтобы найти соответствующий код для изменения.
На практике я видел два вида кода, которые доходят до крайностей по этому принципу:
- Один из них — это то, что абстрактного понятия нет вообще, а повторяющийся код разбросан повсюду, что еще более странно, так это то, что одни абстракции есть, а повторений больше, например, извлечение кода под common.
data.jsФайл обработки данных , некоторые страницы ссылаются на этот файл, а другие страницы полностью копируют несколько разных кодов методов в этом файле. Да и замысел автора смешной - используется только малая часть кода, нет необходимости импортировать весь файл. Вне зависимости от того, сможет ли современный уровень построения интерфейса решить эту проблему, даже если будет введен весь большой файл, эта часть избыточного кода не сильно потеряет производительность после сжатия, но такое поведение копирования везде приводит к последующим затратам на обслуживание. . - Другая причина такого поведения заключается в том, что период построения короткий, а предыдущий код нельзя изменить, поэтому, опасаясь вызвать проблемы с внешней сетью, скопируйте копию той же логики, чтобы изменить ее. Например, логика оплаты, исходная логика представляет собой отдельный плавающий слой пользовательского интерфейса + покупка одной оплаты, теперь продукт предлагает требование «пакетной покупки», логика исходного кода более сложная, и существует явление «неизменяемого» , поэтому пользовательский интерфейс Несколько файлов слоя и логики покупки были скопированы и изменены несколько раз, чтобы сформировать новый модуль «пакетной покупки». Позже продукт предложил «покупку по статье» и, согласно вышеупомянутому «неизменяемому», Впринципе, была сделана еще одна копия модуля "Купить у бара". Таким образом, логика вызывающего сайта будет избыточной и повторяющейся, и необходимо будет вводить различные компоненты пользовательского интерфейса и логику оплаты в соответствии с разными способами покупки.Кроме того, если будут добавлены новые требования, такие как поддержка «оплаты в рассрочку» , то много файлов будет изменено.Самое печальное, что те, кто наконец захочет рефакторить код в один унифицированный вызов, столкнутся с тремя давлениями "исправления" и необходимостью сравнивать и анализировать множество логик для извлечения сходства, и рабочей нагрузкой больше нельзя использовать. Измеряется удвоением, и эта рабочая нагрузка часто не распознается и не понимается продуктом.
- Другая крайность — чрезмерное проектирование.При написании каждой логики она абстрагируется, что значительно снижает удобочитаемость кода.Простой цикл for необходимо использовать повторно, даже определения переменных.Такой вид кода относительно прост в сопровождении. стоимость и чрезмерная абстракция совсем другой логики делают абстрактный метод очень сложным и часто «задействуют все тело», что также нежелательно.
Это также причина, почему этот принцип стоит на первом месте.Рабочая нагрузка рефакторинга, вызванная таким поведением, является самой большой.Поддержание хорошего обслуживания кода является своего рода грамотностью и ответственностью.Если вы уклонитесь или будете лениться в этом отношении, удвоится эту нагрузку на других или себя в будущем.
2. SRP(Single Responsibility Principle)
SRP — это еще и известный принцип проектирования — единая ответственность.В объектно-ориентированном программировании считается, что у класса должна быть единая ответственность, а у класса должна быть только одна мотивация к изменению.
Для фронтенд-разработки самая важная идея заключается в том, что функции должны нести единую ответственность, а функция должна делать только одну вещь, чтобы обеспечить возможность повторного использования функций, и одна функция имеет более сильную возможность повторного использования, а во-вторых, она может сделать общую структуру кода более понятной, а детали инкапсулировать в небольшие функции. Другой момент также связан с единой ответственностью, то есть функциями без побочных эффектов, также известными как чистые функции.Мы должны постараться обеспечить количество чистых функций.Нечистые функции неизбежны, но их следует свести к минимуму.
Поставьте принцип SRP на второе место, потому что это очень важно, никто не хочет видеть бардак логики, при сопровождении кода, если нет четкой логической структуры, все определение данных, обработка данных, манипулирование DOM и т.д. Ряд подробных кодов помещается в одну функцию, что делает эту функцию очень многословной, что вызывает у людей инстинктивное отвращение и нежелание проверять внутреннюю логику.
Вся сложная логика вынесена в функцию, я думаю, все нахмурятся, увидев такой код:
show: function(a, b) {
if (!isInit) {
init();
isInit = true;
}
// reset
this.balance = 0;
this.isAllBalance = false;
var shouldShowLayer = true,
preSelectedTermId = 0,
needAddress = course.address_state,
showTerms,
termsObj;
var hasPunish = false;
this.course = course = course || {};
opt = opt || {};
opt.showMax = opt.showMax || 6;
(isIosApp || b.isIAP) && (usekedian = !0, priceSymbol = '<i class="icon-font i-kedian"></i>'),
f.splice(b.showMax), layer.show({
$container:b.$container,
content:termSelectorTpl({
terms:f,
curTermId:b.curTermId || d,
name:a.name,
hasPunish:h,
userInfo:j
}, {
renderTime:T.render.time.renderCourseTime,
renderCourseTime:renderCourseTime,
hideUserInfo:b.hideUserInfo,
hideTitle:b.hideTitle,
hidePayPrice:b.hidePayPrice,
confirmText:b.confirmText,
sys_time:a.sys_time
}),
cls:"term-select-new",
allowMove:function(a) {
return opt.allowMove || ($target.closest('.select-content').length &&
$('.term-select-new .select-time').height() +
$('.term-select-new .select-address').height() +
$('.term-select-new .select-discounts').height() > (winWidth > 360 ? 190 : winWidth > 320 ? 175 : 150));
},
afterInit:function(c) {
if (needAddress) {
that.loadAddress();
// 如果需要地址,且是 app 的话,屏幕可见性切换时需要更新下地址
if (isApp) {
$(document).on(visibilityChange, function (e) {
// console.log('visibilityChange',document[hidden]);
if (!document[hidden]) {
// true 参数表示必须刷新
that.loadAddress(true);
}
});
}
}
that.afterTermSelect();
$dom.on('click', '.layer-close', function() {
setTimeout(function() {
!opt.noAutoHide && layer.hide();
}, 100);
opt.onCancel && opt.onCancel();
});
$dom.on('click', '.term', function(e) {
var $this = $(this);
var $terms = $('.term');
if (!$this.hasClass('disabled')) {
$terms.removeClass('selected');
$this.addClass('selected');
}
that.afterTermSelect();
});
$dom.on('click', '.layer-comfirm', function(e) {
var $this = $(this);
var termId = $dom.find('.term.selected').data('term-id');
var termName = $dom.find('.term.selected').find('.term-title').html();
var discountId = $dom.find('.discounts-list_item.selected').data('discount-id');
var couId = $dom.find('.discounts-list_item.selected .discounts-coupon').data('cou-id');
var directPay = false;
// ios 手Q IAP
if (that.toRecharge) {
// 需要充值的金额数目
var toRechargePrice = that.curPrice - that.balance;
if (isIosApp) {
require.async('api', function (api) {
api.invoke('api', 'balanceRecharge', {
amount: toRechargePrice
});
// 充值完成设置回调
api.addEventListener('balanceRechargeCallBack', function(data) {
// 支付成功的话
// code=0为成功,其他表示失败
// mode=1表示走充值档位回调,2表示直接充值回调,如果ios 直接充值成功则直接支付
var directPay = data.code === 0 && data.mode === 2;
// 执行回调刷新数据
that.toGetBalance(that.course, termId, function() {
directPay && $this.trigger('click');
});
});
});
} else {
var toRechargePrice = that.curPrice - that.balance;
if (that.rechargeMap &&
Object.keys(that.rechargeMap).indexOf("" + toRechargePrice) > -1) {
that.opt.onComfirmClick && that.opt.onComfirmClick(1);
iosPay.iosRecharge({
productId: that.rechargeMap[toRechargePrice],
count: toRechargePrice,
succ: function() {
that.toGetBalance(that.course, $('.term.selected').data('term-id'));
}
});
} else {
that.opt.onComfirmClick && that.opt.onComfirmClick(2);
// T.jump('/iosRecharge.html?_bid=167&_wv=2147483651');
that.jumpPage('/iosRecharge.html?_bid=167&_wv=2147483651');
}
}
return;
}
if (!termId) {
require.async(['modules/tip/tip'], function(Tip) {
Tip.show(opt.dialogTitle);
});
return true;
}
// check address
if (needAddress && !that.addressid) {
if (course.must_fill_mailing || !$dom.find('.select-address').hasClass('z-no')) {
// 没填地址的话地址框要标红,然后需要滑到视窗让用户看到
var $cnt = $dom.find('.select-content');
var $addressWrap = $dom.find('.select-address_wrapper').addClass('z-err');
var cntRect = $cnt[0].getBoundingClientRect();
var addressBoxRect = $addressWrap[0].getBoundingClientRect();
// console.log('>>>>> ', cntRect, addressBoxRect);
if (addressBoxRect.bottom > cntRect.bottom) {
$cnt.scrollTop($cnt.scrollTop() + (addressBoxRect.bottom - cntRect.bottom));
}
return;
}
}
if (that.isAllBalance && that.opt.onComfirmClick) {
that.opt.onComfirmClick(3);
}
opt.cb && opt.cb(termId, discountId, couId, termName, that.isAllBalance, that.payBalance, that.addressid);
setTimeout(function() {
!opt.noAutoHide && layer.hide();
}, 300);
});
$dom.on('click', '.discounts-list_item', function(e) {
var $this = $(this);
var $discounts = $('.discounts-list_item');
var isSelected = $this.hasClass('selected');
if (!$this.hasClass('disabled')) {
$discounts.removeClass('selected');
$this.addClass(isSelected ? '' : 'selected');
that.setPayPrice();
}
});
$dom.on('click', '.address-person .i-edit2, .address-add', function() {
var termId = $dom.find('.term.selected').data('term-id');
var courseId = that.course.cid;
var src = '/addrEdit.html?_bid=167&_wv=2147483649&ns=1&fr=' + (location.pathname.indexOf('allCourse.html') > -1 ?
4 : location.pathname.indexOf('courseDetail.html') > -1 ? 2 : 3) + '&course_id=' + courseId + '&term_id=' + termId;
// T.jump(src);
that.jumpPage(src);
}).on('click', '.select-address_title .i-right-light', function(e) {
var $addressDom = $dom.find('.select-address');
var isOpen = !$addressDom.hasClass('z-no');
if (isOpen) {
$addressDom.addClass('z-no');
that.theAddressid = that.addressid;
that.addressid = undefined;
} else {
$addressDom.removeClass('z-no');
that.addressid = that.theAddressid;
}
});
}
});
} else {
opt.cb && opt.cb(opt.curTermId || preSelectedTermId);
}
}
Единая ответственность не обязательно должна выполняться многими функциями, но также может быть достигнута с помощью сегментации, например:
show(data) {
data && this.setData(data);
const renderData = {
data: this.data,
courseData: this.data.courseData,
termList: this.termList,
userInfo: this.userInfo,
addrList: this.addrList,
isIAP: this.isIAP,
balance: betterDisplayNum(this.balance),
curPrice: betterDisplayNum(this.curPrice),
curTermId: this.curTermId,
discountList: this.discountList,
curDisId: this.curDisId,
jdSelectId: this.jdSelectId,
curAddrId: this.curAddrId
};
const formatters = {
// formatters
termFormatter,
priceFormatter,
okBtnFormatter,
balanceFormatter,
priceFormatterWithDiscount
};
console.log('[render data]: ', renderData);
const html = payLayerTpl(renderData, formatters);
// 记录滚动条位置
this._setScrollTop();
// 防止重复append
if (this.$view) {
this.$view.replaceWith(html);
} else {
this.$container.append(html);
}
afterUIRender(() => {
this.$view = $('.' + COMPONENT_NAME).show();
this._setContentHeight(); // 动态设置滚动区域的高度
this._restoreScrollTop(); // 恢复滚动位置
this._initEvent();
this._initCountDown(); // 限时折扣倒计时
});
}
Хотя эта функция не несет единой ответственности, внутренняя логика процесса наглядно показана в виде «сегментации», поэтому код будет выглядеть намного лучше, чем все детали в одной функции.
За одну ответственность, все еще трудно поддерживать, главным образом из-за разделения обязанностей. Иногда слишком подробное разделение ответственности принесет большие трудности для чтения. В этом случае лучше сравнивать сописание. Ответственность эквивалентна Пункт «статьи. Для статьи каждый абзац имеет свою центральную идею, которая может быть описана в одном предложении. Если вы обнаружите, что центральная идея функции очень расплывчато, или вам нужно много языка для описания Это, то, возможно, у него уже есть много обязанностей разделить.
3. LKP(Least Knowledge Principle)
Принцип LKP - это принцип наименьшего знания, также известный как «закон Деметры», то есть объект должен иметь наименьшее знание другого объекта, неважно, насколько сложны ваши внутренности, меня волнует только о месте, где был сделан звонок.
Сохранение введения и простоты использования открытого интерфейса также является общим правилом дизайна API.На практике мы обнаружили такой компонент пользовательского интерфейса:
module.exports = {
show: function(course, opt) {
// 此处省略一堆逻辑
},
jumpPage: function(url) {
// 此处省略一堆逻辑
},
afterTermSelect: function() {
// 此处省略一堆逻辑
},
setPrice: function() {
// 此处省略一堆逻辑
},
setBalance: function() {
// 此处省略一堆逻辑
},
toGetBalance: function(course, curTermId, cb) {
// 此处省略一堆逻辑
},
setDiscounts: function(course, curTermId, curPrice) {
// 此处省略一堆逻辑
},
filterDiscounts: function(discounts, curPrice) {
// 此处省略一堆逻辑
},
isSuitCoupon: function(cou, curPrice) {
// 此处省略一堆逻辑
},
setPayPrice: function() {
// 此处省略一堆逻辑
},
setTermTips: function(wording) {
// 此处省略一堆逻辑
},
loadAddress: function(needUpdate) {
// 此处省略一堆逻辑
},
setAddress: function(addressid) {
// 此处省略一堆逻辑
}
}
Этот компонент пользовательского интерфейса предоставляет множество методов, включая бизнес-логику, логику представления и методы инструментов. В настоящее время это вызовет много проблем у сопровождающего. Я инстинктивно думаю, что все эти открытые методы используются, поэтому это немного сложно реорганизовать некоторые из этих методов, но на самом деле методы, вызываемые извне,showВот и все.
Хороший пакет, каким бы сложным ни был интерьер, должен выставлять максимально лаконичный и практичный интерфейс, а внутренняя логика поддерживается самостоятельно.Например, приведенный выше код, как UI-компонент, предоставляет самые основныеshow/hideпри необходимости можно добавить методupdateМетоды самообновляются, не раскрывая многочисленных деталей, что вызывает путаницу у вызывающих и сопровождающих.
4. Фундаментальная теорема о читабельности
Фундаментальная теорема удобочитаемости -«Код должен быть написан таким образом, чтобы свести к минимуму время, необходимое для его понимания другими»..
Стили и принципы кодирования не являются обобщенными. Нам часто приходится выбирать некоторые принципы и схемы кодирования. Например, для выбора троичных выражений, когда мы чувствуем, что обе схемы разумны, то единственным критерием оценки является базовая читабельность. Теорема, независимо от того, насколько великолепно он написан, лучший код — это тот код, который люди в первую очередь могут понять.
5. Значительное имя
Удобочитаемость кода в основном зависит от именования переменных и функций. Хорошее имя может помочь сопровождающим понять логику прямым способом. история увлекательная.
Однако придумать хорошее название пока сложно, тем более, что мы не являемся носителями английского языка, что добавило слой препятствий: некоторые считают, что запутаться в названии приведет к низкой эффективности, а развитие требования должны быть выполнены в первый раз. В этом нет ничего неправильного.В процессе разработки следует ориентироваться на функциональную логику, но не игнорировать нейминг полностью.Так называемое «письмо» нужно упражняться.Чем больше думать, тем естественнее будет нейминг. повлияет на эффективность работы.
Я рекомендую Правила бойскаута, упомянутые дядей Бобом здесь. Каждый раз, когда вы смотрите на свой код, вам нужно проводить рефакторинг. Самый простой рефакторинг - это изменить имя. Может быть, имя поначалу больше подходит, но логика становится все больше и больше. написано. Оно уже не соответствует оригинальному именованию. При просмотре кода мы легко можем переименовать переменные и методы, и современные средства редактирования легко это делают.
Самое страшное - неправильное наименование, например:
function checkTimeConflict(opts) {
if (opts.param.passcard || (T.bom.get('autopay') && T.bom.get('term_id'))) {
selectToPay({
result: {}
}, opts);
} else {
DB.checkTimeConflict({
param: {
course_id: opts.param.courseId,
term_id: opts.param.termId
},
succ: function(data) {
selectToPay(data, opts);
},
err: function(data) {
dealErr(opts, data);
}
});
}
}
Эта функция называетсяcheck*В начале первоначальное намерение состоит в том, чтобы определить, конфликтует ли время курса, но внутренняя логика включает в себя весь процесс оплаты.В это время для вызывающего абонента, если он не внимательно изучит внутреннюю логику, он может ошибочно поверь в этоcheckНет побочных эффектов, которые вызывают аварию.
6. Правильное обслуживание аннотаций
Комментарии — спорная тема, некоторые считают, что читаемые функциональные переменные очень понятны, никаких дополнительных комментариев не требуется, а комментарии неудобны в сопровождении, например:
// 1-PC, 2-android手QH5, 3-android APP, 4-ios&非手QH5, 5-IOS APP
var platform = isAndroidApp ? 3 : isIosApp ? 5 : 4;
На самом деле значение этого поля уже изменилось, но поскольку модификатор только модифицировал логику и не обращал внимания на эту строчку комментариев, старый комментарий давал неверную информацию, а комментарий в это время не просто стал невалидным комментарием , но даже это приведет к непониманию со стороны сопровождающих и вызовет ошибки.
В этом случае либо вести комментарии, либо указывать в комментариях интерфейсную документацию, вести документацию, в остальных случаях нужны соответствующие комментарии, для сложной логики, если есть лаконичный комментарий, он читабелен для кода. Помощь секса велика , но некоторые ненужные комментарии можно удалить.Ключом к выбору комментариев является основная теорема читабельности, например:
const filterFn = (term) => {
if (rule.hideEndTerms && term.is_end) {
return false; // 隐藏已结束的期
}
if (rule.hideSignEndTerms && term.is_out_of_date) {
return false; // 隐藏已结束报名的期
}
if (rule.hideAppliedTerms && courseUtil.isTermApplied(term)) {
return false; // 隐藏已报名的期
}
if (rule.hideZeroAllowedTerms && courseUtil.isTermZero(term)) {
return false; // 隐藏名额已满的期
}
if (rule.productType === productType.PACKAGE) {
return false; // 隐藏课程包的班级
}
return true;
};
Для приведенной выше логики, хотя функциональное значение можно приблизительно угадать через переменные, логическую структуру можно ясно уловить с первого взгляда благодаря краткости и ясности комментариев.
резюме
Принципы, подготовленные шестью кодами, упомянутыми здесь, первые три ориентированы на обслуживание кода, а последние три — на удобочитаемость кода, вся ремонтопригодность и читаемость составляют основную строку кода. Как фронтенд-разработчик, вы хотите иметь хорошую грамотность кода, во-первых, пусть ваш код может поддерживать, не давать огромные затраты и нагрузки другим, во-вторых, старайтесь обеспечить красивый и аккуратный код кода.Люди видят людей любовь, так же, как читать хорошую книгу, чувствуя себя приятно.
"Грамотность кода" отношение, очень любят программистов программирования не будет отсутствовать "качество кода". Обычно мы говорим «написать код» для“程序设计”, вместо "программирования","дизайн"Это слово говорит о том, что наш код — это произведение, может быть, не столь тонкое, как «искусство», но не неряшливое, это «произведение» не имеет никакой декоративной ценности, что не только отражает «непрофессионализм» кодировщиков, но и отражает отношение к программированию.Отношение, чистота и ремонтопригодность кода зависит от того, действительно ли вы «заботитесь» о своем коде.Каждый программист не обязательно любит программирование, но вы должны относиться к своей профессии с «серьезным» отношением.
"clean code"Автор, дядя Боб, упомянул, что кто-то дал ему браслет с надписью «Одержимость испытаниями», и он обнаружил, что не может снять его снова не только потому, что браслет был тугим, но и из-за него. духовное заклинание. При программировании мы подсознательно смотрим на свои запястья, можем ли мы найти невидимый браслет?