javascript代码重构之:写好函数

1、函数要短小,一个函数只做一件事

如果函数做了较多的事情,它就难以组合、测试和推测。同时让函数只做一件事情的时候,它们就很容易重构。

// Bad
function
 showStudent(ssn){
const
 student = db.
get
(ssn);
if
(student !== 
null
){
        document.querySelector(
`#${elementId}`
).innerHTML =
`${student.ssn},
            ${student.firstName},
            ${student.lastName}`
} 
else
{
thrownew
Error
(
'student not found'
)
}
}
showStudent(
'444-44-4444'
)
// Good
function
 findStudent(db,id){
const
 student = db.
get
(id);
if
(student === 
null
){
thrownew
Error
(
'student not found'
);
}
};
function
 getStudentInfo(student){
return
`${student.ssn},${student.firstName},${student.lastName}`
};
function
 showStudentInfo(elementId,info){
    document.querySelector(elementId).innerHTML = info;
}
function
 showStudent(ssn){
const
 student = findStudent(ssn);
let
 studentInfo = getStudentInfo(student);
    showStudentInfo(elementId,studentInfo);
}

只是做了些许的改进,但已开始展现出很多的优势:undefined

2、每个函数一个抽象层级

函数中混杂不同的抽象层级,往往让人迷惑。读者可能无法判断某个表达式是基础概念还是细节。更恶劣的是,就像破损的窗户,一旦细节和基础概念混杂,更多的细节就会在函数中纠结起来。理解抽象层次请参考:抽象层次

// Bad
function
 parseBetterJSAlternative(code) {
let
 REGEXES = [
// ...
];
let
 statements = code.split(
' '
);
let
 tokens;
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
// ...
})
});
let
 ast;
  tokens.forEach((token) => {
// lex...
});
  ast.forEach((node) => {
// parse...
})
}
// Good
function
 tokenize(code) {
let
 REGEXES = [
// ...
];
let
 statements = code.split(
' '
);
let
 tokens;
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
// ...
})
});
return
 tokens;
}
function
 lexer(tokens) {
let
 ast;
  tokens.forEach((token) => {
// lex...
});
return
 ast;
}
function
 parseBetterJSAlternative(code) {
let
 tokens = tokenize(code);
let
 ast = lexer(tokens);
  ast.forEach((node) => {
// parse...
})
}

3、使用描述性的名称

函数越短小,功能越集中,就越便于取个好名字。长而具有描述性的名称,要比短而令人费解的名称好。长而具有描述性的名称,要比描述性的长注释好。使用某种命名约定,让函数名称中的多个单词容易阅读,然后使用这些单词给函数取个能说明其功能的名称。

// Bad
function
 dateAdd(date, month) {
// ...
}
let
 date = 
new
Date
();
// 很难从函数名了解到加了什么
dateAdd(date, 
1
);
function
 write(name){
// ...
}
function
 assertEqual(a,b){
// ...
}
// Good
function
 dateAddMonth(date, month) {
// ...
}
let
 date = 
new
Date
();
dateAddMonth(date, 
1
);
// 告诉我们 name 是一个 field
function
 writeField(name){
// ...
}
// 能更好的解释参数的顺序和意图
function
 assertExpectedEqualActual(expected,actual){
// ...
}

4、函数参数

最理想的参数数量是零,其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。有足够的理由才能使用三个以上参数。如果函数需要三个以上参数,就说明其中一些参数应该放在一个对象中了。参数越多,函数越不容易理解,同时编写能确保参数的各种组合运行正常的测试用例越困难。

5、避免副作用

如果一个函数不是获取一个输入的值并返回其它值,它就有可能产生副作用。这些副作用可能是写入文件、修改一些全局变量、屏幕打印或者日志记录、查询HTML文档、浏览器的cookie或访问数据库。无论哪种情况,都具有破坏性,会导致古怪的时序性耦合及顺序依赖。现在你确实需要在程序中有副作用。像前面提到的那样,你可能需要写入文件。现在你需要做的事情是搞清楚在哪里集中完成这件事情。不要使用几个函数或类来完成写入某个特定文件的工作。采用一个,就一个服务来完成。

// Bad
// 下面的函数使用了全局变量。
// 如果有另一个函数在使用 name,现在可能会因为 name 变成了数组而不能正常运行。
var
 name = 
'Ryan McDermott'
;
function
 splitIntoFirstAndLastName() {
  name = name.split(
' '
);
}
splitIntoFirstAndLastName();
console.log(name); 
// ['Ryan', 'McDermott'];
// Good
function
 splitIntoFirstAndLastName(name) {
return
 name.split(
' '
);
}
var
 name = 
'Ryan McDermott'
var
 newName = splitIntoFirstAndLastName(name);
console.log(name); 
// 'Ryan McDermott';
console.log(newName); 
// ['Ryan', 'McDermott'];

6、删除重复代码

重复代码意味着你要修改某些逻辑的时候要修改不止一个地方的代码。

// Bad
function
 showDeveloperList(developers) {
  developers.forEach(developers => {
var
 expectedSalary = developer.calculateExpectedSalary();
var
 experience = developer.getExperience();
var
 githubLink = developer.getGithubLink();
var
 data = {
      expectedSalary: expectedSalary,
      experience: experience,
      githubLink: githubLink
};
    render(data);
});
}
function
 showManagerList(managers) {
  managers.forEach(manager => {
var
 expectedSalary = manager.calculateExpectedSalary();
var
 experience = manager.getExperience();
var
 portfolio = manager.getMBAProjects();
var
 data = {
      expectedSalary: expectedSalary,
      experience: experience,
      portfolio: portfolio
};
    render(data);
});
}
// Good
function
 showList(employees) {
  employees.forEach(employee => {
var
 expectedSalary = employee.calculateExpectedSalary();
var
 experience = employee.getExperience();
var
 portfolio;
if
(employee.type === 
'manager'
) {
      portfolio = employee.getMBAProjects();
} 
else
{
      portfolio = employee.getGithubLink();
}
var
 data = {
      expectedSalary: expectedSalary,
      experience: experience,
      portfolio: portfolio
};
    render(data);
});
}

7、使用更优雅写法

1、使用默认参数代替短路表达式
// Bad
function
 writeForumComment(subject, body) {
  subject = subject || 
'No Subject'
;
  body = body || 
'No text'
;
}
// Good
function
 writeForumComment(subject = 
'No subject'
, body = 
'No text'
) {
...
}
2、用 Object.assign 设置默认对象
// 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'
,
// User did not include 'body' key
  buttonText: 
'Send'
,
  cancellable: 
true
}
function
 createMenu(config) {
  config = 
Object
.assign({
    title: 
'Foo'
,
    body: 
'Bar'
,
    buttonText: 
'Baz'
,
    cancellable: 
true
}, config);
// 现在 config 等于: {title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true}
// ...
}
createMenu(menuConfig);
3、喜欢上命令式编程之上的函数式编程
// Bad
const
 programmerOutput = [
{
    name: 
'Uncle Bobby'
,
    linesOfCode: 
500
}, {
    name: 
'Suzie Q'
,
    linesOfCode: 
1500
}, {
    name: 
'Jimmy Gosling'
,
    linesOfCode: 
150
}, {
    name: 
'Gracie Hopper'
,
    linesOfCode: 
1000
}
];
var
 totalOutput = 
0
;
for
(
var
 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
}
];
var
 totalOutput = programmerOutput
.map((programmer) => programmer.linesOfCode)
.reduce((acc, linesOfCode) => acc + linesOfCode, 
0
);

8、不要把标记用作函数参数

标记告诉你的用户这个函数做的事情不止一件。但是函数应该只做一件事。如果你的函数中会根据某个布尔参数产生不同的分支,那就拆分这个函数。

// Bad
function
 createFile(name, temp) {
if
(temp) {
    fs.create(
'./temp/'
+ name);
} 
else
{
    fs.create(name);
}
}
// Good
function
 createTempFile(name) {
  fs.create(
'./temp/'
+ name);
}
function
 createFile(name) {
  fs.create(name);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shansec~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值