函数式编程
函数是一等公民
JS编程特点
1.函数式编程和面向对象编程的混编语言
2.可扩展性
函数式编程
优点:易读 易维护
概念 函数是第一类对象 不依赖任何其他对象独立存在
纯函数
相同的输入得到相同的输出 不依赖且不影响外部环境也不产生任何副作用
输出完全取决于输入
下面就是一个纯函数
两次输入的值相同 两次返回的值也相同
var a = 1;
function test(num) {
console.log(num + 1);
}
test(a);
副作用
只要与函数外部环境发生了交互就是副作用
比如 发送数据请求 改变数据 console.log DOM操作 数据的存储(cookie)
var arr1 = ['a', 'b', 'c', 'd', 'e'],
arr2 = ['a', 'b', 'c', 'd', 'e'];
var spArr = arr1.splice(0, 3), //副作用
slArr = arr2.slice(0, 3); //无副作用
console.log(arr1);
console.log(spArr);
console.log(arr2);
console.log(slArr);
函数组合
若干个纯函数 偏函数 柯里化函数组合成一个新的函数 形成数据传递 并实现一种有序执行的效果
function toUpperCase(str) {
return str.toUpperCase();
}
function exclaim(str) {
return str + '!';
}
function compose(f, g) {
return function (x) {
return f(g(x)); //左倾
};
}
var f = compose(exclaim, toUpperCase);
console.log(f('hello'));
当参数有多个呢 怎么进行优化
function toUpperCase(str) {
return str.toUpperCase();
}
function exclaim(str) {
return str + '!';
}
function split(str) {
return str.split('');
}
function reverse(str) {
return str.reverse();
}
function join(str) {
return str.join('-');
}
function compose() {
var args = Array.prototype.slice.call(arguments),
len = args.length - 1;
return function (x) {
var res = args[len](x);
while (len--) {
res = args[len](res);
}
return res;
};
}
var f = compose(exclaim, join, reverse, split, toUpperCase);
console.log(f('hello'));
方式2 使用reduceRight
function toUpperCase(str) {
return str.toUpperCase();
}
function exclaim(str) {
return str + '!';
}
function split(str) {
return str.split('');
}
function reverse(str) {
return str.reverse();
}
function join(str) {
return str.join('-');
}
function compose2() {
var arg2 = Array.prototype.slice.call(arguments);
return function (x) {
return arg2.reduceRight(function (res, cb) {
return cb(res);
}, x);
};
}
var f = compose2(exclaim, join, reverse, split, toUpperCase);
console.log(f('hello'));
左倾
也叫自右向左一次执行
一个函数内部传入一个参数 这个参数是函数执行 这个函数就是左倾的
结合律
在参数中进行函数组合与不分组结果是相同的
function toUpperCase(str) {
return str.toUpperCase();
}
function exclaim(str) {
return str + '!';
}
function split(str) {
return str.split('');
}
function reverse(str) {
return str.reverse();
}
function join(str) {
return str.join('-');
}
function compose2() {
var arg2 = Array.prototype.slice.call(arguments);
return function (x) {
return arg2.reduceRight(function (res, cb) {
return cb(res);
}, x);
};
}
var f = compose2(join, reverse, split);
var f1 = compose2(compose2(join, reverse), split);
var f2 = compose2(join, compose2(reverse, split));
console.log(f('hello'));
console.log(f1('hello'));
console.log(f2('hello'));
pointfree
一些通用的函数组合出各种复杂的运算 上层的运算不要直接操作数据 而通过底层的函数处理
用一种与参数无关的形式合成运算 每一种函数都是一个运算 不需要用代表数据的那个参数
高阶函数
js函数实际上都是指向某一个变量
一个函数可以接收另一个函数作为变量
一个函数接收另一个函数作为参数变量的这个函数就是高阶函数
数组的扩展方法 计时器 sort replace…都是高阶函数
下列函数可以被简化为什么
var test = function (fn) {
return doSth(function (data) {
return fn(data);
});
};
function doSth(fn) {
fn();
}
fn(data) -> function(data){return fn(data)}()
var test = function(fn){
return doSth(fn(data))
}
var test = function(fn){
fn(data)()
}
var test = fn(data)
函数柯里化
1.简化代码
2.提高维护性
3.功能单一化
功能内聚
降低耦合
降低代码的复用性
提高代码的适应性
function add(a, b, c, d) {
return a + b + c + d;
}
add(1, 2, 3, 4);
function curry(fn) {
var _arg = Array.prototype.slice.call(arguments, 1);
return function () {
var newArg = _arg.concat([].slice.call(arguments));
return fn.apply(this, newArg);
};
}
var add2 = curry(add, 1, 2);
console.log(add2(3, 4));
最终优化后的柯里化函数
function curry(fn, len) {
var len = len || fn.length;
var func = function (fn) {
var _arg = Array.prototype.slice.call(arguments, 1);
return function () {
var newArgs = _arg.concat([].slice.call(arguments));
return fn.apply(this, newArgs);
};
};
return function () {
var argLen = arguments.length;
if (argLen < len) {
var formateArr = [fn].concat([].slice.call(arguments));
return curry(func.apply(this, formateArr), len - argLen);
} else {
return fn.apply(this, arguments);
}
};
}
函数只要被这个curry函数包裹后就可以实现柯里化
改造事件监听函数
我们在之前的文章中封装过一个兼容ie浏览器的事件监听函数
function addEvent(el, type, fn, capture) {
if (el.addEventListener) {
el.addEventListener(type, fn, capture);
} else if (el.attachEvent) {
el.attachEvent(
'on' + type,
function () {
fn.call(el);
}
);
} else {
el['on' + type] = fn;
}
}
每次调用这个函数都要进行多次判断
所以这里我们对这个函数进行优化改造
var addEvent = (function () {
if (window.addEventListener) {
return function (el, type, fn, capture) {
el.addEventListener(type, fn, capture);
};
} else if (window.attachEvent) {
return function (el, type, fn) {
el.attachEvent('on' + type, function () {
fn.call(el);
});
};
} else {
return function (el, type, fn) {
el['on' + type] = fn;
};
}
})();