# 高阶函数
# 什么是高阶函数
- 一个函数的 参数 是一个函数 (回调)
- 一个函数 返回 一个函数 (拆分函数)
上面两个任意满足其一就是高阶函数
高阶函数示例:
// 一个函数的参数是一个函数 (回调)
function a() {}
a(() => {});
// 一个函数返回一个函数 (拆分)
function b() {
return function() {};
}
// 例子 函数的before
// 希望将核心的逻辑提取出来,在外面再增加功能
// 重写原型上的方法(扩展)
Function.prototype.before = function(beforeFn) {
return (...arg) => {
// ...arg相当于[1,2,3,4] ...运算符 可以将参数收缩成数组也可以将数组展开为一个个参数
// 箭头函数没有this指向,所以向外层寻找,也没有arguments所以上面用剩余运算符合并成一个数组作为一个参数传入
beforeFn();
this(...arg); // 这里的this就是当前调用的函数 也就是下面的say ...arg是展开运算符 相当于 say(1,2,3,4)
};
};
// AOP 切片编程/装饰 把核心功能(say)抽离出来,在核心基础上增加功能(newSay)
const say = (...arg) => {
// 抽离核心功能
// ...arg是剩余运算符
console.log("说话", arg);
};
const newSay = say.before(() => {
console.log("您好");
});
const newSay1 = say.before(() => {
console.log("hello");
});
newSay(1, 2, 3, 4); // 最后输出 您好 说话
newSay1();
# 函数柯里化
把一个大函数拆分多个函数(大概就是不停的返回函数)
函数柯里化实例:
// 柯里化: 把一个大函数拆分多个函数
// 高阶函数包含柯里化
// 类型判断 Object.prototype.toString.call() 一般实现
// console.log(Object.prototype.toString.call("123")); // [object String]
// console.log(Object.prototype.toString.call([123])); // [object Array]
// 一般封装实现
// const checkType = (content, type) => {
// return Object.prototype.toString.call(content) === `[object ${type}]`;
// };
// const b = checkType(123, "Number");
// console.log(b);
// 柯里化实现 (基础版)
const checkType = type => {
return content => {
return Object.prototype.toString.call(content) === `[object ${type}]`;
};
};
const isString = checkType("String"); // 返回的是内层函数
console.log(isString("123")); // 123是上面方法中的content参数
// 还可以进一步封装
const utils = {};
const types = ["Number", "String", "Boolean", "Array"];
// 下面直接用柯里化封装好的方法实现
// types.forEach(type => {
// utils["is" + type] = checkType(type);
// });
// 函数柯里化怎么实现 通用的柯里化实现 (核心是把函数的参数保留起来,到该用的时候再用)
const add = (a, b, c, d, e) => {
return a + b + c + d + e;
};
const curring = (fn, arr = []) => {
// fn就是add
return (...arg) => {
let len = fn.length; // 函数的length就是参数的个数
arr = arr.concat(arg); // [1,2] [1,2,3,4] [1,2,3,4,5]
if (arr.length < len) {
return curring(fn, arr);
}
return fn(...arr);
};
};
let result = curring(add)(1, 2)(3, 4)(5);
console.log(result);
types.forEach(type => {
utils["is" + type] = curring(checkType)(type);
});
console.log(utils.isArray(123));
# AOP (装饰模式) 将函数进行包装 (before,after)
AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来。其实就是给原函数增加一层,不用管原函数的内部实现。
after
实现:
// 在调用n次之后再执行
// after可以生成新的函数 等待函数执行次数达到我的预期时执行
const after = (times, fn) => () => --times === 0 && fn();
const newAfter = after(3, () => {
console.log("调用三次后执行");
});
newAfter();
newAfter();
newAfter();
// 用上面这种方式可以优雅的处理node的并发问题 先看一下不优雅的方式
const fs = require("fs");
let info = {};
let index = 0;
function out() {
if (index === 2) {
console.log("很low的", info);
}
}
fs.readFile("name.txt", "utf8", (err, data) => {
info["name"] = data;
index++;
out();
});
fs.readFile("age.txt", "utf8", (err, data) => {
info["age"] = data;
index++;
out();
});
// 优雅的实现方式
let afterInfo = {};
const outAfter = after(2, () => console.log("优雅的", afterInfo));
fs.readFile("name.txt", "utf8", (err, data) => {
afterInfo["name"] = data;
outAfter();
});
fs.readFile("age.txt", "utf8", (err, data) => {
afterInfo["age"] = data;
outAfter();
});
// 用发布订阅的方式实现
// 用on订阅,emit来发布实现
let e = {
arr: [],
on(fn) {
this.arr.push(fn);
},
emit() {
this.arr.forEach(fn => fn());
}
};
let infoOnEmit = {};
e.on(() => {
console.log("ok");
});
e.on(() => {
if (Object.keys(infoOnEmit).length === 1) {
console.log("这个订阅会在length为1的时候发布");
}
});
e.on(() => {
if (Object.keys(infoOnEmit).length === 2) {
console.log("发布订阅实现", infoOnEmit);
}
});
fs.readFile("name.txt", "utf8", (err, data) => {
infoOnEmit["name"] = data;
e.emit(); // 发布
});
fs.readFile("age.txt", "utf8", (err, data) => {
infoOnEmit["age"] = data;
e.emit();
});
# 发布订阅模式
(做解耦) 预先定义好一件事,等这件事发生的时候在执行 发布和订阅之间是没有关系的
发布订阅模式的应用 react 事务的封装
// react 事务的改变: 可以在前面和后面同时增加方法
// 开始的时候做某件事 结束的时候再做某件事
// 发布订阅的应用
const perform = (anymethod, wrappers) => {
wrappers.forEach(wrap => {
wrap.initilizae();
});
anymethod();
wrappers.forEach(wrap => {
wrap.close();
});
};
perform(() => {
console.log("核心功能");
}, [
{
initilizae() {
console.log("开始时1");
},
close() {
console.log("结束时1");
}
},
{
initilizae() {
console.log("开始时2");
},
close() {
console.log("结束时2");
}
}
]);
# 观察者模式
- 观察者和被观察者是有联系的
- 被观察者里面存了观察者
- 观察者模式包含了发布订阅模式
// 被观察者
class Subject {
constructor() {
this.arr = [];
this.state = "我不饿";
}
attach(o) {
this.arr.push(o);
}
setState(newState) {
this.state = newState;
this.arr.forEach(o => o.update(newState));
}
}
// 观察者
class Observer {
constructor(name) {
this.name = name;
}
update(newState) {
console.log(`${this.name} 知道了 九儿 ${newState}`);
}
}
let o1 = new Observer("Mopecat");
let o2 = new Observer("Sean");
let s = new Subject("九儿");
s.attach(o1);
s.attach(o2);
s.setState("又饿了");