(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。
对象
(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 强制转换的规则
// 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);
const name = values[i++]
替代
const name = values[i];
i++;
// bad
let stationInfo = Object.assign({}, this.pathArr[index], this.factStepsRouteData[index]);
// good
const stationInfo = {...this.pathArr[index], ...this.factStepsRouteData[index]};
init(data) {
if (!data.length) {
return;
}
requestAnimationFrame(async () => {
const num = 100;
this.data.push(...data.slice(0, num));
this.setData(data.slice(num));
})
},
<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) {
}
let body = {
inPlanOrderList,
rateProjectSettingIds,
...
}
this.dataDetail.forEach(item => {
item.ifCodeReading = item.ifCodeReading === '1';
});
4、案例4
没有适用的方法,应该删除,这样只会增加代码体积,并不利于阅读。
6、案例6
像这种代码注释,应该考虑是否有用,没有用应该删除。
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
};
const body = {
ownerCode: this.ownerCode,
materialAndPacks: [row]
}
const {matTypCode, matName, packageType,
storageType, factoryCode, country} = this.formItem;
materialAndPacks.push({
matTypCode,
matName,
packageType,
storageType,
factoryCode,
country
})
let statusCheck = true;
this.selectDataList.forEach(item => {
if (item.orderStatus === '2') {
statusCheck = false;
}
});
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] = '';
}
}
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
}
const {
customerId,
invoiceTile,
financialOrderCurrency,
settlementCurrency,
invoiceType,
echangeRate} = this.orderSelectList[0];
17、案例17
(1)typeList的声明可以省略
(2)在子组件中getEditInfo方法中detail逻辑已被注释掉,而且只用到其中一个字段值,只获取这一个字段值就行。并在图一中应该判断list是否有数据,不能直接通过下标获取,否则会得到undefined,在接下来的获取字段值时就会报错。
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) {}
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不同而已。
23、案例23
这种props传值的方式太繁重了。可通过vuex或者通过provide/inject方式
以下为provide/inject
24、案例24
像这种字典数据一个一个调用,很影响性能。完全可以通过一个接口获取到所有需要的字典数据。