怎样防止写出烂代码

定名

划定规矩:除非在小于 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();
}
    原文作者:时允
    原文地址: https://segmentfault.com/a/1190000018723993
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞