-
少传参数
如果参数超过两个,使用 ES2015/ES6 的解构语法,不用考虑参数的顺序。某个参数不写也不会产生参数匹配错误
Bad:
searchListTable: function (parameter, type, startTime, endTime, searchInput, pagesize) { }
Good:
searchListTable: function ({parameter, type, startTime, endTime, searchInput, pagesize}) {
}
searchListTable({
type:‘dataset’,
startTime:‘2019’,
endTime:‘2020’,
searchInput:‘bob’,
pagesize:‘100’});
-
只做一件事情
一个函数只有一个事情比较易读,易测,易懂
Bad:
getSchema(res){ this.getSelvalSchemaFields = res.fields; this.encryptColumnsArray = []; this.getSelvalSchemaFields.forEach((item, i) => { if (item.type.toLowerCase().indexOf('string') > -1) { this.encryptColumnsArray.push(item.name); } }) }
Good:
getSchema(res){
res.filter(isStringType).forEach(encryptColumns)
}
isStringType(re){
return re.type.indexOf('string')>-1
}
badfor (var i = 0; i < this.getRuleOutput.length; i++) { if (this.getRuleOutput[i].name == 'outputFields') { this.outputVal[i] = this.addForm.outputField; } if (this.getRuleOutput[i].name == 'qualityType') { this.outputVal[i] = this.addForm.scoreWay } if (this.getRuleOutput[i].name == 'outputLimit') { this.outputVal[i] = this.addForm.badDataLimit; } addForm.inputParams.outputGroup[i] = { "name": this.getRuleOutput[i].name, "value": this.outputVal[i] } }
good
function getOutputVal(name){ return ({ 'outputFields': this.addForm.outputField, 'qualityType': this.addForm.scoreWay, 'outputLimit': this.addForm.badDataLimit, })[name] } for (var i = 0; i < this.getRuleOutput.length; i++) { var outputVal = getOutputVal(this.getRuleOutput[i].name) addForm.inputParams.outputGroup[i] = { "name": this.getRuleOutput[i].name, "value": outputVal } }
-
顾名思义
看函数名就应该知道它是干啥的。
Bad:
function addToDate(date, month) {
}
const date = new Date();
// 很难知道是把什么加到日期中
addToDate(date, 1);Good:
function addMonthToDate(month, date) {
}
const date = new Date();
addMonthToDate(1, date); -
抽象一层
如果函数嵌套过多会导致很难复用以及测试。
Bad:
function parseBetterJSAlternative(code) {
const REGEXES = [
// ...
];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
// ...
});
});
const ast = [];
tokens.forEach((token) => {
// lex...
});
ast.forEach((node) => {
// parse...
});
}Good:
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const ast = lexer(tokens);
ast.forEach((node) => {
// parse...
});
}
function tokenize(code) {
const REGEXES = [
// ...
];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
return tokens;
}
function lexer(tokens) {
const ast = [];
tokens.forEach((token) => {
ast.push( /* ... */ );
});
return ast;
} -
去重
很多时候虽然是同一个功能,但由于一两个不同点,让你不得不写两个几乎相同的函数。
要想优化重复代码需要有较强的抽象能力,错误的抽象还不如重复代码。所以在抽象过程中必须要遵循 SOLID 原则(SOLID 是什么?稍后会详细介绍)。
Bad:
function showDeveloperList(developers) {
developers.forEach((developer) => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers) {
managers.forEach((manager) => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}Good:
function showEmployeeList(employees) {
employees.forEach(employee => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const data = {
expectedSalary,
experience,
};
switch(employee.type) {
case 'develop':
data.githubLink = employee.getGithubLink();
break
case 'manager':
data.portfolio = employee.getMBAProjects();
break
}
render(data);
})
} -
对象设置默认值
Bad:
const menuConfig = {
title: null,
body: 'Bar',
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || 'Foo';
config.body = config.body || 'Bar';
config.buttonText = config.buttonText || 'Baz';
config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);Good:
const menuConfig = {
title: 'Order',
// 'body' key 缺失
buttonText: 'Send',
cancellable: true
};
function createMenu(config) {
config = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// config 就变成了: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
// ...
}
createMenu(menuConfig); -
不要传 flag 参数
通过 flag 的 true 或 false,来判断执行逻辑,违反了一个函数干一件事的原则。
Bad:
function createFile(name, temp) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}Good:
function createFile(name) {
fs.create(name);
}
function createFileTemplate(name) {
createFile(`./temp/${name}`)
} -
避免副作用(第一部分)
函数接收一个值返回一个新值,除此之外的行为我们都称之为副作用,比如修改全局变量、对文件进行 IO 操作等。
当函数确实需要副作用时,比如对文件进行 IO 操作时,请不要用多个函数/类进行文件操作,有且仅用一个函数/类来处理。也就是说副作用需要在唯一的地方处理。
副作用的三大天坑:随意修改可变数据类型、随意分享没有数据结构的状态、没有在统一地方处理副作用。
Bad:
// 全局变量被一个函数引用
// 现在这个变量从字符串变成了数组,如果有其他的函数引用,会发生无法预见的错误。
var name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
name = name.split(' ');
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];Good:
var name = 'Ryan McDermott';
var newName = splitIntoFirstAndLastName(name)
function splitIntoFirstAndLastName(name) {
return name.split(' ');
}
console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott']; -
避免副作用(第二部分)
在 JavaScript 中,基本类型通过赋值传递,对象和数组通过引用传递。以引用传递为例:
假如我们写一个购物车,通过 addItemToCart() 方法添加商品到购物车,修改 购物车数组。此时调用 purchase() 方法购买,由于引用传递,获取的 购物车数组 正好是最新的数据。
看起来没问题对不对?
如果当用户点击购买时,网络出现故障, purchase() 方法一直在重复调用,与此同时用户又添加了新的商品,这时网络又恢复了。那么purchase() 方法获取到 购物车数组 就是错误的。
为了避免这种问题,我们需要在每次新增商品时,克隆 购物车数组 并返回新的数组。
Bad:
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};Good:
const addItemToCart = (cart, item) => {
return [...cart, {item, date: Date.now()}]
}; -
不要写全局方法
在 JavaScript 中,永远不要污染全局,会在生产环境中产生难以预料的 bug。举个例子,比如你在 Array.prototype 上新增一个 diff 方法来判断两个数组的不同。而你同事也打算做类似的事情,不过他的 diff 方法是用来判断两个数组首位元素的不同。很明显你们方法会产生冲突,遇到这类问题我们可以用 ES2015/ES6 的语法来对 Array 进行扩展。
Bad:
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};Good:
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
} -
比起命令式我更喜欢函数式编程
函数式变编程可以让代码的逻辑更清晰更优雅,方便测试。
Bad:
const programmerOutput = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {
totalOutput += programmerOutput[i].linesOfCode;
}Good:
const programmerOutput = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
let totalOutput = programmerOutput
.map(output => output.linesOfCode)
.reduce((totalLines, lines) => totalLines + lines, 0) -
封装条件语句
Bad:
if (fsm.state === 'fetching' && isEmpty(listNode)) {
// ...
}Good:
function shouldShowSpinner(fsm, listNode) {
return fsm.state === 'fetching' && isEmpty(listNode);
}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
// ...
} -
尽量别用“非”条件句
Bad:
function isDOMNodeNotPresent(node) {
// ...
}
if (!isDOMNodeNotPresent(node)) {
// ...
}Good:
function isDOMNodePresent(node) {
// ...
}
if (isDOMNodePresent(node)) {
// ...
} -
避免使用条件语句
Q:不用条件语句写代码是不可能的。
A:绝大多数场景可以用多态替代。
Q:用多态可行,但为什么就不能用条件语句了呢?
A:为了让代码更简洁易读,如果你的函数中出现了条件判断,那么说明你的函数不止干了一件事情,违反了函数单一原则。
Bad:
class Airplane {
// ...
// 获取巡航高度
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
}
}
}Good:
class Airplane {
// ...
}
// 波音777
class Boeing777 extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getPassengerCount();
}
}
// 空军一号
class AirForceOne extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude();
}
}
// 赛纳斯飞机
class Cessna extends Airplane {
// ...
getCruisingAltitude() {
return this.getMaxAltitude() - this.getFuelExpenditure();
}
} -
避免类型检查(第一部分)
JavaScript 是无类型的,意味着你可以传任意类型参数,这种自由度很容易让人困扰,不自觉的就会去检查类型。仔细想想是你真的需要检查类型还是你的 API 设计有问题?
Bad:
function travelToTexas(vehicle) {
if (vehicle instanceof Bicycle) {
vehicle.pedal(this.currentLocation, new Location('texas'));
} else if (vehicle instanceof Car) {
vehicle.drive(this.currentLocation, new Location('texas'));
}
}Good:
function travelToTexas(vehicle) {
vehicle.move(this.currentLocation, new Location('texas'));
} -
避免类型检查(第二部分)
如果你需要做静态类型检查,比如字符串、整数等,推荐使用 TypeScript,不然你的代码会变得又臭又长。
Bad:
function combine(val1, val2) {
if (typeof val1 === 'number' && typeof val2 === 'number' ||
typeof val1 === 'string' && typeof val2 === 'string') {
return val1 + val2;
}
throw new Error('Must be of type String or Number');
}Good:
function combine(val1, val2) {
return val1 + val2;
} -
不要过度优化
现代浏览器已经在底层做了很多优化,过去的很多优化方案都是无效的,会浪费你的时间,想知道现代浏览器优化了哪些内容,请点这里。
Bad:
// 在老的浏览器中,由于 `list.length` 没有做缓存,每次迭代都会去计算,造成不必要开销。
$(function(){$('.addr').html(location.href);$('.addr').attr('href',location.href)})
// 现代浏览器已对此做了优化。
for (let i = 0, len = list.length; i < len; i++) {
// ...
}
js 代码书写规范_函数
最新推荐文章于 2022-06-09 14:15:28 发布