js 代码规范

1、变量声明

(1)使用let或const替换var声明变量。
(2)变量声明前,需要考虑该变量是常量还是一个可变化的,选用合适的方式声明。
原因:var没有块作用域的概念,存在变量声明提升,并可以跨块级作用访问。

// bad 
var a = 1;
// good
let b = 1;
const c = 1;

(3)字符串属性值必须通过单引号括起来,不能用双引号;如果字符串属性值太长,注意换行,用注意需要通过+连接。

// bad
let a = "a";
// good 
let a = 'a';

(4)使用let或const声明每一个变量,不能通过逗号的形式紧跟的声明。并且每一个变量后必须紧跟一个分号。

// bad
const a = 1,
	b = 2;
// good 
const a = 1;
const b = 2;

(5)将let和const进行分组处理

// bad
const a = 1;
let b = 1;
const c = 3;
let d = 2;
// good
const a = 1;
const c = 3;
let b = 1;
let d = 2;

(6)变量的声明要做到,即用即定义,不能把所有的变量都定义在开头,并且要做到意义明确,要别人知道这个变量的作用,不能用a,b,a1等类似作为变量名。以降低代码阅读成本。

(7)禁止使用链式赋值。
js引擎先去作用域链找b这个变量, 如果找到则使用这个b , 如果找不到则抛错(严格模式下) 或者在window作用域下创建一个全局变量b(非严格模式) , b = c 其实是一个 赋值表达式 , 它是有返回值的 , 返回的就是c本身;

// bad
const a = b = c = 1;
// good
const a = 1;
const b = 1;
const c = 1;

(8)初始值。建议声明变量时复制一个初始值。对于引用类型,初始值可赋值为null。

2、引用

对象
(1)使用字面值创建对象。

// bad 
let obj = new Object();
// good
let obj = {};

(2)不能使用保留字作为属性名,如果属性名的命名不符合标识符命名规则的话,需要通过单引号引起来。如果出现此现象,统一所有属性名都用单引号引用起来。
标识符命名规则
以字母、下划线或者$符号开头
由字母、下划线、$符号和数字组成

// bad
let obj = {
   default: { clark: 'kent' },
   private: true,
 };
// good
let obj = {
  defaults: { clark: 'kent' },
  hidden: true,
};

// bad
let obj = {
	name: '',
	age: 12,
	'more-info': ''
};
// good
let obj = {
	'name': '',
	'age': 12,
	'more-info': ''
}

(3)动态属性名,如果属性名是变量,可以通过[]的方式。

const a = 'a'let obj = {
	[a]: true 
};
// 但是不推荐,以下方式
obj[[b]] = false;

(4)简化对象的方法声明

// bad
let obj = {
	getValue: function() {}
};
// good
let obj = {
	getValue() {}
}

(5)简化属性名。如果属性名和属性值的变量名称一致,可省略属性名。

const a = true;
// bad
let obj = {
	a: a
};
// good
let obj = {
	a
}
//

并且简写属性名的数据要放在一块.

// bad 
const a = true;
const b = true;
let obj = {
	name: '',
	a,
	age: 12,
	b
}
// good
let obj = {
	a,
	b,
	name: '',
	age: 12
}

(6)获取属性值。通过点或[]的形式获取值。建议只有在属性名命名不符合规则或属性名为变量是使用[]的方式获取属性值。
原因:[]方式在存取属性值时会进行表达式运行。而点方式是直接存取属性值,理论上执行效率会比数组表示法高。

let obj = {
	name: '',
	'name-info': ''
};
// bad 
obj['name'];
// good
obj.name;
// good 
obj['name-info'];

(7)书写方式

  • 每一组属性单独占一行。
  • 除最后一组属性后不加逗号,其他后面都要紧跟逗号。
// bad
let obj = {
	name: '', age: '', hobby: '',
};
// bad
let obj = {
	name: ''
	, age: ''
	, hobby: ''
};
// good 
let obj = {
	name: '',
	age: '',
	hobby: ''
};

数组
(1)通过字面值的方式创建数组。

// bad
let arr = new Array();
// good
let arr = []

(2)数组操作。禁止通过下标的方式添加数据

// bad
arr[arr.length] = '';
// good
arr.push('');

(3)通过拓展符(…)复制数组。

let arrCopy = [...arr];

(4)通过Array.from实现类数组的转化。类数组

const arr = Array.from(foo);
解构

通过解构减少临时引用属性。
对象

function getFullName(user) {
 const firstName = user.firstName;
 const lastName = user.lastName;
 return `${firstName} ${lastName}`;
}
// good
function getFullName(obj) {
 const { firstName, lastName } = obj;
 return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
 return `${firstName} ${lastName}`;
}

数组

const arr = [1, 2, 3];
// bad
const arr1 = arr[1];
const arr2 = arr[2];
// good
const [arr1, arr2] = arr;
// 两个变量互换值
const a = 1;
const b = 2;
[a, b] = [b, a];

也可以对解构出来的属性名起别名

let obj = {
	name: ''
};
const {name: nameAilas} = obj;
模板字符串

如果字符串中有变量,通过模板字符串替换普通的字符串

const name = 'lihua';
// bad
let str = 'My name is ' + name;
// good
let str = `My name is ${name}`;
函数

(1)声明
对用普通的函数不建议通过函数表达式的方式声明。
原因:函数声明会把整个函数提升,而函数表达式只会把函数的引用变量名提升

// bad
const fun = funtion() {};
// good 
function fun() {};

(2)不要把参数命名为 arguments。这将取代原来函数作用域内的 arguments 对象。

// bad
function fun(name, arguments) {};
// good
function fun(name, options, args) {};

(3)不建议使用arguments。可通过(…)替换。
原因:arguments是一个伪数组,不是一个真正的数组。

// bad
function fun() {
   const args = Array.prototype.slice.call(arguments);
   return args.join('');
};
// good 
function fun(...args) {
    return args.join('');
};

(4)直接给函数的参数指定默认值,不要使用一个变化的函数参数。

// bad
function fun (opt) {
	opt = opt || {};
};
// good 
function fun (opt = {}) {};

(5)通过箭头函数简写普通函数

// bad
[1, 2, 3].map(function(item, index) {});
// good
[1, 2, 3].map((item, index) => {});
// 当只有一个参数时,可去掉括号
[1, 2, 3].map(item => {});
// 当函数体只有一个return返回时,可去掉花括号
[1, 2, 3].map(item => item > 2);
类型

通过class替换构造函数

// bad
function Fun(name) {
	this.name = name;
}
// good
class Fun() {
   constructor(name) {
     this.name = name ;
   }
};
模块

使用ES6中import/export 替换 require

// bad
const modal = requrie('');
// good
import modal from '';
// best
import { modal } from '';

全局公共函数文件,不建议export一个对象的方式,而是export一个个函数的形式。这样可以实现按需导入的。

// bad
util.fun = function() {};
export default util;
// bad
export default {
	fun
}
// good
export function fun() {};
import {fun} from '';
高阶函数

通过高阶函数替换for-of。

const arr = [1, 2, 3];
// bad
let sum = 0;
for (let num of arr) {
  sum += num;
};
// good
arr.forEach((num) => sum += num);
// beat 
sum = arr.reduce((total, num) => total + num, 0)

了解每一个高阶函数的特性,根据逻辑需要选取合适的高价函数,而不是一股脑的使用。

// 如果只是遍历
const arr = [1, 2, 3];
// bad 
arr.map(item => {});
// good
arr.forEach(item => {});

如果数据量特别大的话,尤其是遍历操作,推荐使用普通for循环,不建议使用forEach等高价函数,因为高阶函数底层封装的也是for循环,循环调用回调函数。

const arr = [];
// beat
for (let i = 0, len = arr.length; i < len; i++) {}
条件判断

优选使用 === 和 !==,而不是 == 和 !=
通过方法 ToBoolean 强制转换的规则

  • 引用类型 被计算为 true
  • undefined 和 null 为false
  • 布尔值被计算为布尔值
  • number类型,如果为-0,+0 或 NaN 被计算为 false,其他的为 true
  • string 类型,如果为空字符串,被计算为 false,其他的为 true
// bad
if (name !== '') {
}
// good
if (name) {
}
// bad
if (arr.length > 0) {
}
// good
if (arr.length) {}

对NaN,不能通过 === 或 == 进行判断,而是通过isNaN()方法。
原因:任何数据都不等于 NaN,包括自身。

const name;
// bad
if (name === NaN) {
}
// good
if (isNaN(name)) {
}

一个变量对于不能数据的多次判断。

const status = 1;
// bad
if (status === 1 || status === 2 || status === 3) {
}
// good
if ([1, 2, 3].includes(status)) {
}

对于过多的else-if 可以根据逻辑需要,用switch-case替换if。但是switch最后建议加一个default:break; 默认条件。

const num = 99;
// bad
if (num === 10) {
} else if (num === 20) {
} else if (num === 30) {
} else {
}
// good
switch(num) {
	case: 10:
	break;
	case: 20:
	break;
	case: 30;
	break;
	default:
	break;
}

对于复杂的判断建议将其拿出来。

// bad
if (oldPath && (srcX == oldPath.srcX && srcY == oldPath.srcY
            && tarX == oldPath.tarX && tarY == oldPath.tarY)) {}
// good 
const flag = srcX == oldPath.srcX && srcY == oldPath.srcY
            && tarX == oldPath.tarX && tarY == oldPath.tarY;
if (oldPath && flag) {
}
代码块

if后必须带有大括号包裹所有的多行代码块。

// bad
if (name)
	return;
// good
if (name) {
	return;
}
运算

推荐使用赋值运算符

let text = 'hello';
// bad
text = text + 'world';
// good
text += 'world';
注释

多行注释用 /** */
单行注释用 //

// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

  // ...stuff...

  return element;
}
// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

  // ...stuff...

  return element;
}

单行注释。在对象上面另起一行使用单行注释。在注释前插入空行。

// bad
const active = true;  // is current tab
// good
// is current tab
const active = true;

使用 FIXME 标注问题。
使用 TODO 标注解决方式。

function fun() {
	// FIXME: shouldn't use a global here
	total = 0;
}
function fun() {
 	// TODO: total should be configurable by an options param
	let total = 0;
}
空格

大括号前必要有一个空格

// bad
if (){}
// good
if () {}

在控制语句(if、switch 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。

// bad
if() {}
// good
if () {}

使用空格把运算符隔开。

// bad
const x=y+5;
// good
const x = y + 5;

在使用长方法链时进行缩进。使用前面的点 . 强调这是方法调用而不是新语句。

// bad
 $('#items').find('.selected').highlight().end().find('.open').updateCount();
// good
 $('#items')
   .find('.selected')
   .highlight()
   .end()
   .find('.open')
   .updateCount();

单行对象在 {} 内部前后各加一个空格。

// bad
const {firstName, lastName} = obj;
// good
const { firstName, lastName } = obj;
类型

类型检测优先使用 typeof。对象类型检测使用 instanceof。null 或 undefined 的检测使用 == null。

// string
typeof variable === 'string'
// number
typeof variable === 'number'
// boolean
typeof variable === 'boolean'
// Function
typeof variable === 'function'
// Object
typeof variable === 'object'
// RegExp
variable instanceof RegExp
// Array
variable instanceof Array
// 对于Array与Object 推荐使用如下
if (Array.isArray(arr)) {}
// null
variable === null
// null or undefined
variable == null
// undefined
typeof variable === 'undefined'

类型转换
转换成 string 时,使用 + ‘’

// good
num + '';
// bad
new String(num);
num.toString();
String(num);

转换成 number 时,通常使用 +

// good
+str;
// bad
Number(str);

string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt。

var width = '200px';
parseInt(width, 10);

转换成 boolean 时,使用 !!

let num = 3.14;
!!num;

number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt。

// good
var num = 3.14;
Math.ceil(num);
// bad
var num = 3.14;
parseInt(num, 10);
其他
  • 避免全局查找。会沿着作用域链查询,尤其不要再for循环中使用全局属性,应该在局部作用域中保存对全局属性的引用。
  • 如果循环次数有限的话,建议直接多次调用函数,这样会更快。
  • 减少js语句数量,能合并的语句尽量合并合并成一条语句。
  • 迭代性值(递增或者递减),都要尽可能使用组合语句
const name = values[i++]
替代
const name = values[i];
i++;
  • 没有用到引用,方法,变量以及参数,建议删除。
  • 注释掉的代码,建议删除。
  • 不要乱使用console打印日志。
  • 使用解构替换 Object.assign 赋值
    • 浅拷贝
      • 解构
      • Object.assign
      • Object.create 通过构建原型链
    • 深拷贝
      • JSON.parse(JSON.stringify(obj))
      • 递归算法
// bad
let stationInfo = Object.assign({}, this.pathArr[index], this.factStepsRouteData[index]);

// good 
const stationInfo = {...this.pathArr[index], ...this.factStepsRouteData[index]};
Vue 优化
  • Vue 中尽量避免一次性渲染大量数据,采用分批渲染效果会更好。
    如表格加载过量数据,可以采用分批异步渲染
init(data) {
    if (!data.length) {
		return;
	}
    requestAnimationFrame(async () => {
        const num = 100;
        this.data.push(...data.slice(0, num));
        this.setData(data.slice(num));
    })
},
  • 采用组件化。这样不仅利于我们维护,而且在性能上也会有很大的提升。
    在 Vue2.x 之后 Vue 的响应式是以组件为粒度进行更新的,只要修改了当前组件中所使用的数据,组件就会整个去进行更新(主要在diff算法)。
    如,一个表格加载过量数据,这是在这个组件进行按钮操作,修改data中的数据,vue就会将该组件整个重新渲染一遍。所以,应将按钮操作单独提取出来。
  • 无论是动态插槽还是静态插槽,为了保持数据的准确性,子组件都会被强制刷新渲染。可用作用域插槽替代。静态插槽内容的生成是在父组件内完成,而作用域插槽里面的内容是封装在一个 fn 的函数里面,这个不同之处就是作用域插槽的内容不会在父组件中生成,而是等待子组件去调用 fn 函数,在子组件里面生成内容,这也就意味着只有当作用域里插槽面所使用的数据发生变化时,子组件才会被通知更新。
<div>
	静态插槽
</div>

<template #[myName]>
  	动态插槽
</template>

<template #default>
  作用域插槽
</template>
// 静态插槽编译
// 编译代码
function render() {
  with(this) {
    return _c('MyTable', {
      attrs: {
        "keys": keys,
        "tableData": tableData,
        "columns": columns
      }
    }, [_v("静态插槽")])
  }
}

// 作用域插槽编译
function render() {
  with(this) {
    return _c('MyTable', {
      attrs: {
        "keys": keys,
        "tableData": tableData,
        "columns": columns
      },
      scopedSlots: _u([{
        key: "default",
        fn: function () {
          return [_v("作用域插槽")]
        },
        proxy: true
      }])
    })
  }
}
案例

1、案例1
在这里插入图片描述
冗余的判断,此处应该只需要判断是否为真值即可。但是还要注意一下变量是否为0的情况。

if (item.qualityFinishTime) {
}

2、案例2在这里插入图片描述
简化属性名

let body = {
	inPlanOrderList,
	rateProjectSettingIds,
	...
}

3、案例3
在这里插入图片描述
高阶函数使用不正确;不必要的判断;没有分号

this.dataDetail.forEach(item => {
	item.ifCodeReading = item.ifCodeReading === '1';
});

4、案例4
图1
图2
没有适用的方法,应该删除,这样只会增加代码体积,并不利于阅读。

5、案例5
在这里插入图片描述
不必要的console打印,语句后没有分号。

6、案例6
在这里插入图片描述
像这种代码注释,应该考虑是否有用,没有用应该删除。

7、案例7
在这里插入图片描述
这种深度克隆没有必要。

let carList = [
  {
    carNo: this.formItem.carNo,
    driver: this.formItem.driver,
    tel: this.formItem.tel,
    carBusinessType: 'in'
  }
]
if (this.formItem.businessType === 'move') {
  carList.push({
    carNo: this.formItem.outCarNo,
    driver: this.formItem.outDriver,
    tel: this.formItem.outTel,
    carBusinessType: 'out'
  })
}
const body = {
	wmsInPlanDTO: this.formItem,
	wmsInCarInfoDTOList: carList,
	wmsInPlanDetailDTOList: this.data,
	wmsQualityDTOList: this.qualityDataList,
	codecDynamicFeeRateDTOS: this.selectChargeList
};

8、案例8
在这里插入图片描述
没有必要声明 dataList 变量

const body = {
    ownerCode: this.ownerCode,
    materialAndPacks: [row]
}

9、案例9
在这里插入图片描述
解构是一种浅复制赋值。

const {matTypCode, matName, packageType, 
storageType, factoryCode, country} = this.formItem;
materialAndPacks.push({
  matTypCode,
  matName,
  packageType,
  storageType,
  factoryCode,
  country
})

10、案例10
在这里插入图片描述
为什么需要判断数组的长度,直接循环不行吗?

let statusCheck = true;
this.selectDataList.forEach(item => {
	if (item.orderStatus === '2') {
		statusCheck = false;
	}
});

11、案例11
在这里插入图片描述

if ([10, 11].includs(index)) {
  const values = data.map(item => +item[column.property]);
  if (!values.every(value => isNaN(value))) {
    switch (index) {
      case 10:
        sums[index] = `${this.totalData.changeQtySum}`;
        break;
      case 11:
        sums[index] = `${this.totalData.transferWeightSum}KG`;
        break;
      default:
      	break;
    }
  } else {
    sums[index] = '';
  }
}

12、案例12
在这里插入图片描述
注释问题。

13、案例13
在这里插入图片描述
这种判断没有必要吧,直接赋值就行

body.receivableCustomerCode = this.formItem.customerCode

14、案例14
在这里插入图片描述
(1)newVal与oldVal两个参数根本没有用到。
(2)body的声明与赋值
(3)对于if这个判断有必要吗,只有在this.formItem和this.data1都为undefined的是否if判断才为false。

data1: {
 handler(newVal) {
   const body = {
   	codecFeeRateProjectDTO: this.formItem,
   	codecFeeRateProjectSettingDTOS: newVal
   };
   this.updateEditData({
	   	id: this.id, 
	   	body, 
	   	hasSaved: this.hasSaved
	});
 },
 deep: true
}

15、案例15
在这里插入图片描述
这种可以通过解构方式。

const {
	customerId, 
	invoiceTile,
	financialOrderCurrency,
	settlementCurrency,
	invoiceType,
	echangeRate} = this.orderSelectList[0];

16、案例16
在这里插入图片描述
这种数据的转换,大部分应该在后端处理。

17、案例17
在这里插入图片描述
在这里插入图片描述
(1)typeList的声明可以省略
(2)在子组件中getEditInfo方法中detail逻辑已被注释掉,而且只用到其中一个字段值,只获取这一个字段值就行。并在图一中应该判断list是否有数据,不能直接通过下标获取,否则会得到undefined,在接下来的获取字段值时就会报错。

18、案例18
在这里插入图片描述
这种三元判断,可以通过初始值和if判断替换

let load = 'addBillLoad';
let msg = '是否生成账单';
if (ifApply) {
	load = 'addBillApplyLoad';
	msg = '是否生成账单并提交?';
}
或者
const load = ifApply ? 'addBillApplyLoad' : 'addBillLoad';
const msg = ifApply ? '是否生成账单并提交?' : '是否生成账单';

19、案例19
在这里插入图片描述
indexOf 与 includes 公共混淆了吧
indexOf 返回的是下标,没有返回-1
includes 是否存在,返回的是布尔值

if (this.listQuery.containerNumArr.includes(',')) {}if (this.listQuery.containerNumArr.indexOf(',') !== -1) {}

20、案例20
在这里插入图片描述
可否改造成以下

list.forEach(item => {
	const {
		chargeType, 
		chargeTypeCode, 
		chargeType1, 
		chargeTypeCode1,
		...
	} = item;

	item[chargeType] = chargeTypeCode;
	item[chargeType1] = chargeTypeCode1;
	....
	// 多了这层循环操作
	['', 'undefined', 'null'].forEach(it => {
		if (item.hasOwnProperty(it)) {
			delete item[it];
		}
	});
});

21、案例21
在这里插入图片描述(1)if判断条件优化。
(2)数组数据的添加,完全不用if判断,两者之间只有transportStatus不同而已。

22、案例22
在这里插入图片描述
变量声明,分号。

23、案例23
在这里插入图片描述
这种props传值的方式太繁重了。可通过vuex或者通过provide/inject方式
以下为provide/inject
父组件
子组件
minix文件

24、案例24
在这里插入图片描述
像这种字典数据一个一个调用,很影响性能。完全可以通过一个接口获取到所有需要的字典数据。

最后注意
  • 不要声明无用的变量和代码,做到每一句,每一行代码有用。
  • 在写之前,要根据逻辑构思好每一句代码。
  • 选用合适的高阶函数或方法处理数据。
  • 代码排版要条理清晰,关键逻辑处,注释要明确思路。
  • HTML编写要合理,不能一股脑写,避免重复HTML语句。
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值