JS函数——函数式编程


前言

随着对前端开发学习的深入,发现函数式编程相较于面向过程、面向对象的编程规范,更为贴合JavaScript的开发标准。


一、什么是函数式编程?

	首先,函数式编程和面向过程、面向对象编程一样,是一种编程规范;
	其次,它可以把功能分解为一系列的独立的函数,通过函数间的互相调用来完成功能。

二、为什么使用函数式编程?

1. 函数式编程能提高复用性和可拓展性

	每一个函数就是一块积木,我们可以随时拼入新的积木,随时能够取出积木去复用。

//假设我们现在需要 将一个数字乘以2,再减去1。

//面向对象(ES6中有了类,设置了相关的关键字,这里还是采用传统的组合继承的方式编写。)
	function CalcNumber(number) {
		this.number = number;
	}
	CalcNumber.prototype.calc = function(){
		return this.number * 2 - 1;
	}
	//假设此时我们需要 将一个数字乘以2,再减去1,再加上2。
	function CalcNumberSon(number) {
		CalcNumber.call(this, number);
	}
	CalcNumberSon.prototype = new CalcNumber();
	CalcNumberSon.prototype.calc = function(){
		return this.number * 2 - 1 + 2;
	}
//函数式编程
	function multiplyTwo(num) {
		return num * 2;
	}
	function minusOne(num) {
		return num - 1;
	}
	//假设此时我们需要 将一个数字乘以2,再减去1,再加上2。
	function addTwo(num) {
		return num + 2;
	}
	var result = multiplyTwo(10);
	result = minusOne(result );
	result = addTwo(result );

//由以上对比我们可以看出,当需求改变时,函数式编程更加灵活,可复用性、可拓展性更强。

2. Tree-shaking(摇晃树webpack中的功能)

	Tree-shaking的本质是通过文档流的引入判断是否使用某个方法(就是没有用到的部分,不进行最后的打包操作),但是面向对象的编程方案无法记录。

3. 如何写好函数式编程?

3.1. 写好函数式编程的要求

3.1.1. 保证纯函数
	一个函数的返回结果只依赖于他的参数,同样的输入必定有同样的输出

// 非纯函数
var a = 10;
function add(num) {
	return a + num;	//这个函数的返回结果依赖于外部的a变量,当外部的a变量发生改变时,传入相同的参数,不一定会得到相同的结果。
}
add(5)	//结果是15
a = 5;
add(5)	//结果是10

//纯函数
var a = 10;
function add(a, num) {	//在这里我们将a也作为参数传进来,这样就满足了我们纯函数的定义。
	return a + num;	
}

3.1.2. 减少函数副作用
	函数副作用就是函数会影响外部的数据(例如:全局变量)。

// 副作用
var a = 10;
function aPlus() {
	a += 1;	//我们这里的操作,明显改变了外部变量a的值
	return a;	
}

//解决上面的副作用
var a = 10;
function aPlus(a) {	//在这里我们同样也将a作为参数传进来,这样函数里面的变量就不会影响到全局的a变量了。
	a += 1;
	return a;	
}

//当传入的参数为引用类型时(例如:对象、数组),我们可以先进行一次拷贝(例如:使用ES6的Object.create()方法、拓展运算符[...arr]),再执行操作。

4.工程化下的函数式编程


//ES6导出(mode.js)
export function fn1() {

}
export function fn2() {

}
//ES6导入
import * as all from "./mode.js"
import {fn1} from "./mode.js"

//commonjs导出
function fn1() {

}
function fn2() {

}
exports.fn1 = fn1;
exports.fn2 = fn2;
//commonjs导入
const all = require("./mode.js");
const fn1 = require("./mode.js").fn1;

5.函数式编程后,执行中的一些问题

5.1. 值的传递,写起来不方便

	我们如果要连续的执行一系列函数,并且传递计算某个值,会写起来很难受。

5.2. 连续调用,写起来很麻烦

	连续调用一系列函数,写起来很麻烦。

例如:


function multiplyTwo(num) {
	return num * 2;
}
function minusOne(num) {
	return num - 1;
}
function addTwo(num) {
	return num + 2;
}
function addThree(num) {
	return num + 3;
}
var result = multiplyTwo(10);
result = minusOne(result );
result = addTwo(result );
result = addThree(result );

6. Compose函数和Pipe函数

6.1. Compose函数

	Compose函数可以理解为,为了方便我们连续执行方法,把自己调用传值的过程封装了起来,我们只需要给compose函数我们要执行的哪些方法,它会自动执行。

function multiplyTwo(num) {
	return num * 2;
}
function minusOne(num) {
	return num - 1;
}
function addTwo(num) {
	return num + 2;
}
function addThree(num) {
	return num + 3;
}
//var result = multiplyTwo(10);
//result = minusOne(result );
//result = addTwo(result );
//result = addThree(result );

//Compose函数
function compose() {
	const args = [].slice.apply(arguments);	//将参数列表转化为数组;
	//slice() 方法可从已有的数组中返回选定的元素。原数组不改变返回新选定的元素组成的新数组;
	//apply方法将[].slice方法的this指向了arguments。
	return function (num) {
		var _result = num;
		//普通方式
		/*for (var i = args.length - 1; i >= 0; i--) {	//传入参数的执行顺序,从右往左
			_result = args[i](_result);
		}
		return _result;*/
		//采用js封装好的高阶函数reduceRight()方法(和 reduce() 功能是一样的,不同的是 reduceRight() 从数组的末尾向前执行)。
		return args.reduceRight((res,cb) => cb(res),num);	//第一个参数(res,cb) => cb(res),初始值num。
		//res是初始值, 或者计算结束后的返回值。
		//cb是当前元素(也就是传进来要执行的函数)。
	}
}
compose(addThree,addTwo,minusOne,multiplyTwo)(10);

6.2. Pipe函数

	Pipe函数和Compose函数功能一样,不同的是Pipe函数传入参数的执行顺序,从左往右。

function multiplyTwo(num) {
	return num * 2;
}
function minusOne(num) {
	return num - 1;
}
function addTwo(num) {
	return num + 2;
}
function addThree(num) {
	return num + 3;
}
//var result = multiplyTwo(10);
//result = minusOne(result );
//result = addTwo(result );
/result = addThree(result );

//Pipe函数
function pipe() {
	const args = [].slice.apply(arguments);	//将参数列表转化为数组;
	//slice() 方法可从已有的数组中返回选定的元素。原数组不改变返回新选定的元素组成的新数组;
	//apply方法将[].slice方法的this指向了arguments。
	return function (num) {
		var _result = num;
		return args.reduce((res,cb) => cb(res),num);
	}
}
pipe(multiplyTwo,minusOne,addTwo,addThree)(10);

7. 链式调用

	我们可以用promise来组织成一个链式调用(*这里的链式调用和面向对象的链式调用有区别——面向对象链式调用是return this;)。

function multiplyTwo(num) {
	return num * 2;
}
function minusOne(num) {
	return num - 1;
}
function addTwo(num) {
	return num + 2;
}
function addThree(num) {
	return num + 3;
}
//var result = multiplyTwo(10);
//result = minusOne(result );
//result = addTwo(result );
//result = addThree(result );

//用promise组织链式调用
Promise.resolve(10).then(multiplyTwo).then(minusOne).then(addTwo).then(addThree).then((res)=>{
	console.log(res);
})


总结

革命尚未成功,同志仍需努力。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值