重构
我们先来看一段重构问题,这里有一些js片段
function validateSsn(ssn){
if(/^\d{3}-\d{2}-\d{4}$/.exec(ssn)){
console.log('Valid SSN');
} else {
console.log('Invalid SSN');
}
}
function validatePhone(phone){
if(/^(\d{3})-\d{3}-\d{4}$/.exec(phone)){
console.log('Valid Phone Number');
} else {
console.log('Invalid Phone Number');
}
}
我们发现两个函数功能相同,只有一小部分不一样。正常我们考虑抽离出变化的参数value, regular expression, 和打印的message。
重构后的代码
function validateValue(value, regex, type){
if(regex.exec(value)){
console.log('Valid' + type);
} else {
console.log('Invalid' + type);
}
}
使用一个函数要比使用两个函数要好一些,这使得代码整洁并且可维护。例如,发现一个bug,只需要修改一个地方,而不是搜索整个代码库来查找可能粘贴并修改整个函数的地方。
但是有如下的情况会怎么样
function valiadateAddress(address){
if(parseAddress(address)){
console.log('Valid Address');
} else {
console.log('Invalid Address');
}
}
function valiadateName(name){
if(parseFullName(name)){
console.log('Valid Name');
} else {
console.log('Invalid Name');
}
}
在这parseAddress和parseFullName函数都是接受一个string,如果传入的字符串符合预订的句法则返回true。我们该如何来重构这段代码?我们可以像前面一样使用value来代表address和name,使用type来代表’Address’和’Name’,但我们的正则表达式在这里是一个函数
如果我们可以将一个函数作为参数传递
高阶函数
许多语言不支持将函数作为函数传递,有的支持但变得非常复杂。而在函数式语言中,函数是这个语言中的一等公民,换句话说,函数仅仅是一种值而已。由于函数仅仅是值,我们可以将它们作为参数传递:
function valiateValueWithFunc(value, parseFunc, type){
// 是不是想起了我们平时传callback进去的时候
if(pareseFunc(value)){
console.log('Invalid' + type);
} else {
console.log('Valid' + type);
}
}
我们的新函数就称为高阶函数(Higher-order-Function)。
高阶函数可以接受函数作为参数,或者返回一个函数结果,或者两者具备。
接下来我们看一下我们的正则表达式,每次都要添加.exec
到它的末尾,可以通过创建一个返回exec函数的高阶函数来防止这种情况
function makeRegexParser(regex){
return regex.exec;
}
// 原本是var parseSsn = /^\d{3}-\d{2}-\d{4}$/.exec;
var parseSsn = makeRegexParser(/^\d{3}-\d{2}-\d{4}$/);
valiateValueWithFunc('...', parseSsn, 'SSN');
的确这是一个微不足道的改进,但是这里展示了一个高阶函数返回一个函数的例子。
下面再看另一个例子
function makeAdder(constantValue){
return function add(value){
return constantValue + value;
}
}
看一下它怎么使用
// 保存一个函数引用
var add10 = makeAdder(10);
// 调用它
console.log(add10(20)); // 30
console.log(add10(30)); // 40
add函数通过闭包保存了constantValue的值。