函数式编程
- 纯函数: 不要在函数里直接使用全局变量,这会成为这个函数的隐试依赖, 如果有需要就从参数里传进去
- 相同的输入,总是能得到相同的输出. 无副作用.
- 有副作用的地方比如操作dom节点, 发起请求等.单独使用一个函数抽出来. 不要和别的纯函数放在一起.
- 严格的控制输入,输出的数据类型. 尽量保证输入,输出的值的数据类型是稳定的
- 管道函数: 将数据的处理过程分割成一个个小函数用pipe组合起来;
- 将复杂的流程分割, 把分支逻辑用curry函数组合;
柯里化函数的使用场景
柯里化函数就像手机一样. 遇到它之前觉得没什么. 遇到它之后就再也离不开了.
柯里化函数的使用场景一般来说有两种.
1. 函数的参数本身就具有逻辑复杂度.用柯里化包装复杂参数复用代码.
2. 高阶函数. 函数的参数是预先确定好的. 但是我们需要添加参数来使函数更有复用性.
函数的参数本身就具有逻辑复杂度
我们想要验证一串数字是否是正确的手机号和邮箱号
function checkPhone(phoneNumber) {
return /^1[34578]d{9}$/.test(phoneNumber);
}
function checkEmail(email) {
return /^(w)+(.w+)*@(w)+((.w+)+)$/.test(email);
}
可能会遇到验证身份证号,验证密码等各种验证信息,因此在实践中,为了统一逻辑,,我们就会封装一个更为通用的函数,将用于验证的正则与将要被验证的字符串作为参数传入
function check(targetString, reg) {
return reg.test(targetString);
}
但是这样封装之后,在使用时又会稍微麻烦一点,因为会总是输入一串正则
check(/^1[34578]d{9}$/, '14900000088');
check(/^(w)+(.w+)*@(w)+((.w+)+)$/, 'test@163.com');
那么这个时候,我们就可以借助柯里化,在check的基础上再做一层封装,以简化使用
var _check = createCurry(check);
var checkPhone = _check(/^1[34578]d{9}$/);
var checkEmail = _check(/^(w)+(.w+)*@(w)+((.w+)+)$/);
checkPhone('183888888');
checkEmail('xxxxx@test.com');
高阶函数
在我们日常的开发中, 高阶函数的使用是已经相当频繁了. ajax的回调函数. 各种事件的回调函数. 特别是在进行函数式编程中各式各样的回调函数比如pipe.
在这些高阶函数中. 有一些是公共方法. 我们没办法修改他们传入的参数. 而如果此时我们的函数又需要添加参数用来增加复用性式. 柯里化函数就有了用武之地.
比如有两个ajax请求.
$.ajax({
url: "url1"
success: function(data) {
var type = "url1"
console.log(type, data)
}
})
$.ajax({
url: "url2"
success: function(data) {
var type = "url2"
console.log(type, data)
}
})
在这两个ajax请求中他们的回调函数是相似的. 只有变量type不同其他都相同. 但是因为回调函数的参数是一定的. 我们没办法把type从参数里穿进去.我们可以写两个函数.但是这样我们修改的时候就要两个一起改 我们也可以把 type='url1'和 type="url2". 封装到两个函数中. 其他相同的部分封装到一个函数中. 但是这样我们就平白多出了两个函数. 这时我们就可以把回调变成柯里化函数.
var callback = curry(function(type, data) {
console.log(type, data)
})
$.ajax({
url: "url1",
success: callback("url1"),
})
$.ajax({
url: "url2",
success: callback("url2"),
})
pipe函数的使用场景
pipe函数在处理数据, 表单验证时, 效果非常好
var data = {
result: "SUCCESS",
interfaceVersion: "1.0.3",
requested: "10/17/2013 15:31:20",
lastUpdated: "10/16/2013 10:52:39",
tasks: [
{id: 104, complete: false, priority: "high",
dueDate: "2013-11-29", username: "Scott",
title: "Do something", created: "9/22/2013"},
{id: 105, complete: false, priority: "medium",
dueDate: "2013-11-22", username: "Lena",
title: "Do something else", created: "9/22/2013"},
]
};
var fetchData = function () {
return Promise.resolve(data);
};
var getIncompleteTaskSummaries = function(membername) {
return fetchData()
.then(R.prop('tasks'))
.then(R.filter(R.propEq('username', membername)))
.then(R.reject(R.propEq('complete', true)))
.then(R.map(R.pick(['id', 'dueDate', 'title', 'priority'])))
.then(R.sortBy(R.prop('dueDate')));
};
常用的函数式编程方法
{
curry : function(func, numArgs) {
numArgs = numArgs || func.length;
function subCurry(prev) {
return function() {
var funcArgs = Array.prototype.slice.apply(arguments)
var args = prev.concat(funcArgs);
if (args.length < numArgs) {
return subCurry(args)
} else {
return func.apply(null, args);
}
}
}
return subCurry([]);
},
pipe : function() {
var funcArr = [].slice.apply(arguments);
return function(data) {
return funcArr.reduce(function(res, fn) {
return fn(res)
}, data)
}
},
prop: function(str, obj) {
return obj[verity.isString(str)];
},
propEq: function(prop, val, obj) {
return obj[verity.isString(prop)] === val;
},
pick: function(arr, obj) {
return verity.reduce(function(res, prop) {
if (obj[prop] !== undefined) {
res[prop] = obj[prop]
}
return res;
}, verity.isArray(arr), {})
},
}