这里写自定义目录标题
1. 对于多个条件,使用 Array.includes
假设我们想要在函数中检查任务类型taskType是nrm 还是 pm。那么代码可能是这样的:
const checkTaskType = (taskType) => {
if(taskType === 'nrm' || taskType === 'pm') {
console.log('tasktype checked')
}
}
checkTaskType('nrm'); // 输出 'tasktype checked'
考虑到我们只有两个模型,这么做似乎也还能接受,但如果我们还想要检查另一个或者是几个模型呢?如果我们增加更多 '||'语句,那么代码将变得难以维护,且不够整洁。为了让它更加简洁,我们可以像这样重写函数:
const checkOmcType = (omcType) => {
if(['nrm', 'pm'].includes(omcType)) {
console.log('omcType checked')
}
}
checkOmcType('pm'); // 输出 'omcType checked'
上面的代码看起来已经很漂亮了。为了更进一步改善它,我们可以创建一个变量来存放所有的任务类型:
const checkOmcType = (omcType) => {
const omcTypes = ['nrm', 'pm', 'alarm']
if(omcTypes.includes(omcType)) {
console.log('omcType checked')
} else console.log('omcType checked fail')
}
checkOmcType('alarm'); // 输出 'omcType checked'
checkOmcType('test'); // 不输出 'omcType checked fail'
现在,如果我们想要检查更多的任务类型,只需要添加一个新的元素即可。此外,如果它很重要的话,我们还可以将 omcTypes变量定义在函数作用域外,并在需要的地方重用。这种方式可以让我们集中管理,并使维护变得轻而易举,因为我们只需在代码中更改一个位置。
2. 匹配所有条件,使用 Array.every 或者 Array.find
在本例中,我们想要检查传入的任务状态是否是已有的状态类型。为了以更加命令式的方式实现,我们也许会这么做:
const omcTaskStatus = [
{name: '待采集', value: 'INITIALING'},
{name: '采集中', value: 'RUNNING'},
{name: '停止', value: 'STOPPED'},
{name: '失败', value: 'ERROR'}
]
const checkEveryTaskStatus = (taskStatusValue) => {
let isValid = false
for (let omcTask of omcTaskStatus) {
if (isValid) {
break
}
isValid = omcTask.value === taskStatusValue
}
return isValid
}
console.log(checkEveryTaskStatus('test')) // 输出 false
console.log(checkEveryTaskStatus('RUNNING')) // 输出 true
如果你不关心其背后发生了什么,那么你可以重写上面的函数并使用Array.some来达到相同的结果。
const checkEveryTaskStatus = (taskStatusValue) => {
return omcTaskStatus.some(omcTask => omcTask.value === ta
}
console.log(checkEveryTaskStatus(‘test’)) // 输出 false
console.log(checkEveryTaskStatus(‘RUNNING’)) // 输出 true
3. 使用索引或者映射,而不是 switch 语句
假设我们想通过传入omc类型,获取omc中包含的任务类型
// 获取资源、性能、告警中包含的任务类型
const getTaskTypeByOmc3 = (state) => {
switch (state) {
case 'nrm':
return ['ftp', 'gsmFile']
case 'pm':
return ['ftp', 'database', 'gsmDatabase']
case 'alarm':
return ['socket', 'corba', 'gsmAlarm']
default:
return []
}
}
上诉代码可以重构,完全去除 switch 语句。
const taskType = new Map()
.set('nrm', ['ftp', 'gsmFile'])
.set('pm', ['ftp', 'database', 'gsmDatabase'])
.set('alarm', ['socket', 'corba', 'gsmAlarm'])
const taskType1 = {
nrm: ['ftp', 'gsmFile'],
pm: ['ftp', 'database', 'gsmDatabase'],
alarm: ['socket', 'corba', 'gsmAlarm'],
}
const getTaskTypeByOmc = (omcType) => {
return taskType.get(omcType) || []
}
console.log(getTaskTypeByOmc()); // 输出 []
console.log(getTaskTypeByOmc('nrm')); // 输出 ['ftp', 'gsmFile']
console.log(getTaskTypeByOmc('')); // 输出 []
4. 平铺多维数组
使用 Spread(展开),可以很容易去平铺嵌套多维数组:
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = [].concat(...arr); //=> [11, 22, 33, 44, 55, 66]
可惜,上面的方法仅仅适用于二维数组。不过,通过递归,我们可以平铺任意维度的嵌套数组。
function flattenArray(arr) {
const flattened = [].concat(...arr);
return flattened.some(item => Array.isArray(item)) ?
flattenArray(flattened) : flattened;
}
const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
const flatArr = flattenArray(arr);
//=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
5. 函数默认值
doSomething({ foo: 'Hello', bar: 'Hey!', baz: 42 });
// bad
function doSomething(config) {
const foo = config.foo !== undefined ? config.foo : 'Hi';
const bar = config.bar !== undefined ? config.bar : 'Yo!';
const baz = config.baz !== undefined ? config.baz : 13;
}
// good
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 }) {
...
}
// better
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 } = {}) {
...
}
6. arguments 转数组
// bad
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// good
const sortNumbers = (...numbers) => numbers.sort();
7. 构建对象
剔除部分属性,将剩下的属性构建一个新的对象
// bad
function pick(data) {
const { id, name, age} = data
const res = { guid: id }
if (name) {
res.name = name
}
else if (age) {
res.age = age
}
return res
}
// good
function pick({id, name, age}) {
return {
guid: id,
...(name && {name}),
...(age && {age})
}
}
8. 对象的基本解构
// bad
handleEvent = () => {
this.setState({
data: this.state.data.set("key", "value")
})
};
// good
handleEvent = () => {
this.setState(({data}) => ({
data: data.set("key", "value")
}))
};
9. 对象深度解构
// bad
function test(fruit) {
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}
// good
function test({name} = {}) {
console.log (name || 'unknown');
}
10. 数组解构
// bad
const splitLocale = locale.split("-");
const language = splitLocale[0];
const country = splitLocale[1];
// good
const [language, country] = locale.split('-');
11. optional-chaining
const obj = {
foo: {
bar: {
baz: 42,
},
},
};
const baz = obj?.foo?.bar?.baz; // 42
同样支持函数:
function test() {
return 42;
}
test?.(); // 42
exists?.(); // undefined
需要添加 @babel/plugin-proposal-optional-chaining 插件支持
12. 对象解构
12.1 删除不需要的属性
有时候你不希望保留某些对象属性,你可能会枚举整个对象然后删除它们,但实际上只需要简单的将这些无用属性赋值给变量,然后把想要保留的有用部分作为剩余参数就可以了。
下面的代码里,我们希望删除id,provinceName和name参数,只保留network相关的信息。
let {id, name, provinceName,...networkObject} = {
id: 396,
name: "collenciton-test16",
provinceName: "上海",
networkElement: "MME",
networkType: "4G",
networkVersion: "V1",
networkName: "核心网",
};
console.log(networkObject); // {networkElement: "MME",networkType: "4G",networkVersion: "V1",networkName: "核心网"}
13. 必须传值的解构参数
function setCookie(name, value, {
secure,
path,
domain,
expires
}) {
// 设置cookie的代码
}
setCookie('type', 'js') // 报错
在此函数内,name与value参数是必须的,而secure、path、domain与expires则不是。默认情况下调用函数时未给参数解构传值会抛出错误。像上例中如果setCookie不传第三个参数,就会报错。
若解构参数是可选的,可以给解构的参数提供默认值来处理这种错误。
function setCookie(name, value, {
secure,
path,
domain,
expires
} = {}) {
// 设置cookie的代码
}
setCookie('type', 'js') // 不会报错
14. 在函数参数中解构嵌套对象
使用类似于对象字面量的语法,可以深入到嵌套的对象结构中去提取你想要的数据。
在下面的代码中,rows是对象scanLog中嵌套的一个对象。如果我们对rows的createTime属性感兴趣,使用解构赋值可以很轻松地得到它。
var scanLog = {
id: '000012138',
rows: {
clazz: 'com.chinamobile.cmss.oss4.ftpscanner.entrance.FtpScannerTask',
turbo: true,
createTime: 1562893502465
},
tags: ['tag']
}
const idAndCreateTime = ({id, rows: {createTime}}) => {
console.log(`id: ${id} createTime: ${createTime}`);
}
idAndCreateTime(scanLog); // => id: 000012138 createTime: 1562893502465
15. 合并对象
ES6带来了扩展运算符(…)。它一般被用来解构数组,但你也可以用它处理对象。
可以使用扩展运算符来展开一个新的对象,第二个对象中的属性值会改写第一个对象的属性值。比如object2的b和c就会改写object1的同名属性。
let array1 = [{name: '服务器北向IP地址', value: 'host'},
{name: '端口号', value: 'port'}]
let array2 = [{name: '文件接口协议', value: 'omcProtocol'},
{name: '文件目录', value: 'dir'}]
let mergedArray = […object1, …object2]
console.log(merged)
// [{name: '服务器北向IP地址', value: 'host'},{name: '端口号', value: 'port'},{name: '文件接口协议', value: 'omcProtocol'}, {name: '文件目录', value: 'dir'}]
16. Sets
使用Set实现数组去重
在ES6中,因为Set只存储唯一值,所以你可以使用Set删除重复项。
let arr = [1, 1, 2, 2, 3, 3];
let deduped = [...new Set(arr)] // [1, 2, 3]
17. 验证函数的简化示例
function validate(values) {
if(!values.first)
return false;
if(!values.last)
return false;
return true;
}
console.log(validate({first:'Bruce',last:'Wayne'})); // true
上面的函数完美的完成验证工作。但是当有很多表单,则需要应用验证,此时会有不同的字段和规则。如果可以构建一个在运行时配置的通用验证函数,会是一个好选择。
// object validation rules
const schema = {
first: {
required:true
},
last: {
required:true
}
}
// universal validation function
const validate = (schema, values) => {
for(field in schema) {
if(schema[field].required) {
if(!values[field]) {
return false;
}
}
}
return true;
}
console.log(validate(schema, {first:'Bruce'})); // false
console.log(validate(schema, {first:'Bruce',last:'Wayne'})); // true
现在有了这个验证函数,我们就可以在所有窗体中重用,而无需为每个窗体编写自定义验证函数。