定名
划定规矩:除非在小于 5 行的函数里,不然不要运用单字定名变量
申明:寄义不清楚,不能做到「望文生义」
BadCode
var l = data.length;
GoodCode
// 闭包只要一行代码,可以运用单字变量
data.map(d => d.length)
划定规矩:不要运用名词加数字的定名要领
申明:寄义不清楚,不能做到「望文生义」
BadCode
var obj = {};
var obj2 = {...obj, key: 1};
GoodCode
var obj = {};
var objWithKey = {...obj, key: 1};
划定规矩:应该且只要要领和函数以动词开首
此处动词没有包含时态
变量名应该是名词或许名词短语。
破例:
- 回调函数
- 性命周期函数
- 框架级别函数
- getter/setter
申明:
- 函数的定名须要表现内部事变
- 值变量以动词定名轻易让人误会为是一个匿名函数
BadCode
// 以名词开首,看不邃晓什么有什么功用
function option() {}
// 时态不对
function updatedTime() {}
GoodCode
function selectOption() {}
function updateTime() {}
划定规矩:防止运用拼音或许缩写定名。
破例:
- 专有名词:weixin/POI
- 传统商定:i/j/k 示意轮回索引
申明:寄义不清楚,不能做到「望文生义」
BadCode
var uo = function updateOrder(){}
var as = [].slice;
var ex = Object.extends;
var nu = number
GoodCode
// weixin/wx 是专有名词
var weixinUser = {};
var wx = weixin;
// POI 是专有名词
var poi = {};
划定规矩:称号是非应与其作用域大小相对应。
破例:专有 API,如 alert
申明:在上层作用域下的代码,会被更多函数运用到。其称号应该只管长或许通用,以保证可以搜刮到。
BadCode
// 在全局变量上定义了一个 item 变量,然则很难从定名上明白其作用是什么。
window.item = {}
GoodCode
window.primaryProductItem = {};
不要在变量/函数尾部加标记、数字
申明:变量中加标记,每每是为了商定其优先级或许作用域。标记应该在变量名前面。
BadCode
function getDot_(){}
function privateFn$$ (){}
GoodCode
function _getDot() {}
function $$privateFn() {}
划定规矩:实例称号要和类名相干
申明:类作为实例的所属,其称号表达的寄义要一脉相承
BadCode
class Person() {}
var dog = new Person(); // dog is a Person ?
GoodCode
class Person() {}
var jack = new Person();
划定规矩:防止直白的中英文翻译
申明:粗犷的翻译,更轻易形成误会,还不如写拼音
BadCode
// 衬着「页面顶部的实行人」
// 照样衬着「实行砍头的人」?
function renderHeadExecutantPeople(){}
GoodCode
function renderHeader() {}
划定规矩:观点的定名要一以贯之
申明:防止通一个观点在差别的代码用多种差别的单词形貌。
BadCode
// 长途要求这个观点,前后用了 get/fetch/query
// 三个名词去形貌
function getUserInfo() {}
function fetchProductInfo() {}
function queryPayment() {}
GoodCode
// 统一用 get 形貌
// 浏览代码的人能体会到个中的共性
function getUserInfo() {}
function getProductInfo() {}
function getPayment() {}
划定规矩:别用双关语
破例:专有词
申明:双关语轻易引发歧义
BadCode
// 定单范例,照样排序范例?
var orderType
GoodCode
var sortType
划定规矩:定名须要和完成一致
申明:定名每每是完成的隐喻,假如存在差别则会让浏览者看不懂代码。
BadCode
// empty 的定名寄义和完成截然相反
// (我真的见过这类代码)
function getProduct(id) {
axios.delete('/product', {id});
}
GoodCode
function deleteProduct(id) {
axios.delete('/product', {id});
}
划定规矩:关于布尔值的定名,须要默许其为「真」
申明:布尔变量的称号中,假如加上 「not」之类的否定词,则相当于做了一次了逻辑推断。
BadCode
const notEmpty = !!array.length;
GoodCode
const empty = !array.length;
函数
划定规矩:长度不能凌驾 20 行
申明:代码太长申明做的事变不够专注,同时也会让浏览变得很难题。
划定规矩:Don’t repeat yourself
申明:一样功用的代码不要反复三次
划定规矩:每一个函数只做一件事变,并做好这件事
申明:代码里的逻辑分支要只管少,只做一件事变,而且要处置惩罚好边境和非常状况。
划定规矩:只管削减函数的参数,包含 opitons、config 等参数
申明:函数的输入越多,每每就代表功用约庞杂
划定规矩:解释出来项目/营业的坑
申明:关于比较新鲜的营业逻辑,或许由于体系、接口原因此写的比较新鲜的逻辑。要经由过程解释标注出来
BadCode
framework.doSomeThing();
framework.reset(); // 浏览者心田 OS:这里为啥要做一次 reset?
framework.continueSomeThing();
GoodCode
framework.doSomeThing();
// framework 有个 bug,这里必需要做一次 rest 附链接: http://github.com/issuse/***
framework.reset();
framework.continueSomeThing();
划定规矩:函数要只管「纯」没有副作用
申明:纯函数比较好测试,逻辑也比较清楚,可以宁神的引入和删除。
BadCode
let status;
function method() {
if (status) { ... }
}
GoodCode
function method(status) {
if (status) { ... }
}
划定规矩:函数最好不要修正参数内的数据
申明:修正参数会致使函数的作用变得不可展望
BadCode
function updateObj(obj, value) {
obj.key = value;
return obj;
}
GoodCode
function updateObj(obj, value) {
return {...obj, key: value};
}
划定规矩:除非是 class 的要领,不然不要接见 this
申明:this 的指向常常不牢固,会致使代码难以明白。假如挪用方不熟悉的话,很轻易引发 Bug。
BadCode
function method() {
console.log(this.value);
}
method() // 报错
var obj = { method, value: 1}
obj.method() // 输出 1
GoodCode
function method(value) {
console.log(value);
}
划定规矩:处置惩罚毛病
申明:毛病也是一种逻辑分支,假如不处置惩罚的话,代码就不够硬朗。前端代码处置惩罚毛病的体式格局平常为提醒用户有非常发作。假如毛病不影响营业流程,则写入日记里并上报。
BadCode
function method(data) {
try { return JSON.parse(data) }
catch (e) {}
}
GoodCode
function method(data) {
try { return JSON.parse(data) }
catch (e) {
alert('数据处置惩罚失利')
}
}
数据
划定规矩:不要有 Magic Number
申明:magic number 是指直接在代码中硬编码的数字,每每具有一些营业寄义。
如许会致使:
- 数字的意义难以明白
- 数值要修改时,要改许多处所
BadCode
if (status === 1) {
...
} else if (type === 4) {
...
}
GoodCode
enum Status {
Closed
}
enum Type {
Array
}
if (status === Status.Closed) {
...
} else if (type === Type.Array) {
...
}
划定规矩:不管是 react state 照样 vue data 寄存的营业数据都要具有原子性。
申明:原子性意味着自力,且不可分割。别的属性都由原子营业属性推导、盘算而来,如许能保证状况的一致。
BadCode
// 当 status 为 open 的时刻展现弹窗
// 别的状况则隐蔽弹窗
{
data() {
return {
showAlert: false,
status: 'closed',
}
},
onStatusChange() {
if (status === 'open') {
this.showAlert = true;
} else {
this.showAlert = false;
}
}
}
GoodCode
// showAlert 为非原子的状况
// 其状况可以由 status 推导而来
{
data() {
return {
status: 'closed',
}
},
computed: {
showAlert() {
return this.status === 'open';
}
}
}
划定规矩:关于 react state 和 vue data,应该辨别营业状况和 UI 状况
申明:
- 状况和 UI 存储在一起,有时刻传给后端的数据里会夹杂着没有必要的 UI 状况。
- 营业代码和 UI 代码耦合在一起,营业代码没法复用。
BadCode
// 在一个列表中,用户可以对数据做多选
// 然后删除他们
class extends React.Component {
async componentDidMount() {
const listData = getData();
this.setState({ listData })
}
check = (item) => {
const listData = this.state.listData.map(i => {
if (i === item) {
return {...item, checked: true}
}
return i;
});
this.setState({ listData });
}
delete() {
// 返回给后端的数据结构,会多出一个 checked 字段
deleteItems(this.state.listData.filter(i => i.checked));
}
render() {
const list = this.state.listData.map(item => {
const className = ['item'];
if (item.checked) className.push('active');
return <label
className={className}
onClick={() => this.check(item)}
>{item.naem}</label>;
});
return <>
{list}
<button onClick={this.delete}>delete</button>
</>
}
}
GoodCode
// 在一个列表中,用户可以对数据做多选
// 然后删除他们
class extends React.Component {
async componentDidMount() {
const listData = getData();
// 运用自力的 selected 来保留 UI 状况
this.setState({ listData, selected: [] })
}
check = (item) => {
let { selected } = this.state;
selected = selected.findOrInsert(s => s.id, item);
this.setState({ selected });
}
delete() {
const { selected, listData } = this.state;
deleteItems(listData.filter(i => selected.includes(i.id))));
}
render() {
const { selected, listData } = this.state;
const list = listData.map(item => {
const className = ['item'];
if (selected.includes(item.id)) className.push('active');
return <label
className={className}
onClick={() => this.check(item)}
>{item.naem}</label>;
});
return <>
{list}
<button onClick={this.delete}>delete</button>
</>
}
}
划定规矩:关于 react 运用,防止在 render 的时刻修正状况
申明:react 的 render 应该是纯函数,在 render 里运转 setState 会致使反复衬着,或许死轮回。
BadCode
// 假如 type 为 http 的话,则自动转换为 https
class extends React.Component {
render() {
const { type } = this.state;
if (type === 'http') {
this.setState({ type: 'https'})
}
return <label>{type}</label>;
}
}
GoodCode
// 假如 type 为 http 的话,则自动转换为 https
class extends React.Component {
get type() {
const { type } = this.state;
if (type === 'http') return 'https';
return type;
}
render() {
const type = this.type;
return <label>{type}</label>;
}
}
划定规矩:关于双向绑定运用,防止数据轮回依靠。
申明:
- 轮回依靠轻则致使页面响应慢,重则致使涌现脏数据。
- 防止轮回依靠的前提是理清营业逻辑,搞清楚数据之间的依靠关联。
- 轮回依靠也是双向绑定手艺的诟病之一。
BadCode
// foo 和 bar 相互依靠,致使了死轮回
{
data() {
return {
foo: 1,
};
},
computed: {
bar() {
return this.foo + 1;
}
},
watch() {
bar() {
this.foo = this.bar + 1;
},
}
}
划定规矩:接见数据时,须要斟酌边境状况和 JS 弱范例的特征。
申明:比方用双等号做推断
BadCode
const foo = '0';
const bar = 0
// 做数据推断时不能用双等号
foo == bar // true
foo ? 1 : 2 // 1
bar ? 1 : 2 // 2
// 仅经由过程变量有无 length 来推断是不是为数组
if(obj.length) {
obj.forEach(...)
}
GoodCode
const foo = '0';
const bar = 0
foo === bar // false
if (Array.isArray(obj)) {
obj.forEach(...)
}
划定规矩:不要在遍历数组的同时,转变数组数据
申明:如许做会致使数据的非常。假如须要做这类操纵,最好运用数组函数,或许操纵拷贝数据。
BadCode
const array = [1,2,3,4,5,6,7,8,9,10];
// 删除数组中的偶数
for (var i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) array.splice(i);
}
// array 变成了 [1]
GoodCode
const array = [1,2,3,4,5,6,7,8,9,10];
array.filter(a => !(a % 2))
API
划定规矩:对 setTimeout 挪用时,通报的时候参数必需有意义。
申明:
大多数场景下,setTimeout 背面通报一个时候是为了先实行后续的 A 代码,再延后实行代码闭包里的 B 代码,如右侧示例代码。
但假如跟着营业迭代,A 被改成异步,或许实行时候很长的话。之前做的耽误实行的防备步伐就时效了,或许反而 B 会比 A 先实行。
BadCode
// 代码的原本企图是让 B 延后实行
setTimeout(() => {
B();
}, 1000);
A();
// 代码的企图是让 render 鄙人一帧实行
// 然则差别装备,一帧时候是不牢固的
setTimeout(() => {
render()
}, 16);
GoodCode
// A 函数内要想方法运用 Promise 串接起来
await A();
b();
// 运用体系供应的 API 实行动画帧
requestAnimationFrame(() => {
render();
});
划定规矩:不要运用陈腐的 API
申明:陈腐的 API 每每有许多题目,比方平安、机能、不容易读等。
BadCode
// 推断是不是为数组
Object.prototype.toString.call(array) === "[object Array]"
// 查找数组里第一个偶数
for (var i = 0; i < array.length; i++) {
if (array[i] % 2 === 0) return array[i];
}
// 遍历对象的 key
for (var key in obj) {
console.log(key);
}
// 推断字符串/数组是不是包含
'some text'.indexOf('some') >= 0
// 去除首位空格
' some text '.replace(/(^\s+|\s+$)/g, '')
// 新建对象/数组
const array = new Array();
const obj = new Object();
GoodCode
Array.isArray(array)
array.find(a => a % 2 === 0);
Object.keys(obj).forEach(console.log)
'some text'.includes('some')
' some text '.trim()
const array = [];
const obj = {};
划定规矩:关于 99.9% 的场景,你都不须要运用 React ref
申明:
React Ref 平常是用来处置惩罚和原生 DOM 交互的场景,比方 canvas。
大部分关于 React ref 的运用都是毛病的,大多都拿来用来掌握子元素。这类场景我们更引荐用数据流(redux,mobx)或许用状况提拔去做。
React 官方有对「状况提拔」的形貌 https://react.docschina.org/d…
BadCode
class List extends React.Component {
async refresh() {
this.setState({
items: getItems(),
});
}
render() {
return this.state.items.map(i => <label>{i}</label>);
}
}
class extends React.Component {
onRefresh = () => {
// 用 ref 去挪用子元素的要领
this.list.refresh();
}
render() {
return <>
<List ref={l => this.list = l}></List>
<button onClick={this.onRefresh}/>
</>;
}
}
GoodCode
class List extends React.Component {
render() {
return this.props.items.map(i => <label>{i}</label>);
}
}
class extends React.Component {
// 把数据状况提拔到父组件做操纵
refresh = async () => {
this.setState({
items: getItems(),
});
}
render() {
return <>
<List items{this.state.items}></List>
<button onClick={this.refresh}/>
</>;
}
}
划定规矩:不要用字符串拼接 url
申明:
字符串拼接 url 须要处置惩罚 encode 或许 decode 的状况,另有关于 ?和 # 的推断不对的话,很轻易形成破绽或许 Bug。
现在浏览器和 Node 都已供应了规范的 URL 剖析要领。
https://developer.mozilla.org…
BadCode
// 这段代码既没有对 key、value 做 encode
// 也没有斟酌 url 中 # 涌现的状况
const url = location.href;
if (url.indexOf('?') >= 0) {
return url + key + '=' + value;
} else {
return url + '?' + key + '=' + value;
}
GoodCode
// 运用规范的 URL 剖析,风险会下降许多
const url = new URL(urlStr);
url.searchParams.set(key, value);
return url.toString();
逻辑
划定规矩:判真不判假
申明:
我们应该希冀 if 前提内是个「真」值,而不是一个「假」值。
第二种状况会致使代码不容易明白。
解决方法参考 布尔逻辑
BadCode
// if 前提内希冀的是一个「假」值
if (!(status !== Closed) { ... }
if (!(status !== Closed || type !== Array)) { ...}
GoodCode
if (status === Closed) { ... }
if (status === Closed && type === Array) { ... }
划定规矩:if 前提中,不容易涌现凌驾 3 个逻辑操纵符。
破例:if 前提里可以被 「且」(&&)逻辑拆分红多个子前提
申明:庞杂的前提推断会让代码不容易明白,逻辑上有破绽的话轻易引发 Bug。
解决方法:声明中心变量
BadCode
if (srcElem != dropElem && (srcElem.nextSibling || srcElem.nextElementSibling) != dropElem) {...}
if (selectedItem || (selectedEmployee && selectedEmployee.empId && selectedEmployee) || employee) { ... }
GoodCode
const nextSibling = srcElem.nextSibling || srcElem.nextElementSibling
if (srcElem != dropElem && nextSibling != dropElem ) {
...
}
// 庞杂的逻辑推断可以经由过程 && 做拆分
if (
!Array.isArray(cur)
&& cur != null
&& typeof src[key] === 'object'
&& typeof cur === 'object'
) { ... }
划定规矩:不要用嵌套的三元表达式
申明:
人们浏览嵌套三元表达式时,轻易殽杂语法的优先级,进而致使明白错代码的寄义。
关于这类状况,发起改成 if else。
假如是在 react render 里,则发起自力成函数。
BadCode
function render(props) {
const value = props.value;
return <>
{value < 10 ? value > 0 ? value : 200 - value : 100 - value}
</>;
}
GoodCode
function getValue(value) {
if (value < 10) {
if (value > 0) return value;
return 200 - value;
}
return 100 - value;
}
function render(props) {
const value = props.value;
return <>
{getValue(value)}
</>;
}
划定规矩:if 前提逻辑嵌套不要凌驾三层
申明:过深的嵌套会致使明白难题。
解决方法:兼并推断前提,或许自力成函数。
BadCode
if (status = Opened) {
if (type = 'array') {
if (code = Success) {
doSomething();
}
}
}
GoodCode
if (status = Opened && type = 'array' &&code = Success) {
doSomething();
}