小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
关于函数
什么是高阶函数
- 一个函数的参数,是一个函数(回调)
- 一个函数返回一个函数(拆分/匿名函数)
before函数
把核心代码提取出来,在外面增加功能
// 重写原型上的方法
Function.prototype.before = function(beforeFn) {
return (...args) => { //箭头函数中没有this指向,没有arguments,所以会向上级作用域查找,
beforeFn();
this(...args);
}
}
// AOP 切片 装饰
const say = (...args)=> {
console.log('说话',args);
}
let newSay = say.before(() => {
console.log('您好');
})
let newSay1 = say.before(() => {
console.log('天气很好');
})
newSay(1,2,3)
newSay1()
say函数是核心函数,在函数的原型上添加的方法,所有的函数都会共有这个方法.beforeFn是调用中say.before(()=>{})箭头函数,也就是一个函数的参数,是一个函数- 函数原型上的
before方法return一个函数,也就是一个函数返回一个函数. - 因为箭头函数没有this指向,所以say调用before方法时,this指向say,
- 因为箭头函数没有
arguments参数,所有所传参数向上查找也就是say函数上的参数...args
React事务
开始的时候 做某件事 结束的时候再做某件事
/**
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>
*/
对当前核心函数,做包装,核心函数是anyMethod,在它的外层可以嵌套无限层包装的函数,一层包装中包括
initialize初始化方法和close方法,函数执行时,先执行初始化方法,再执行核心函数方法,最后执行关闭方法.
const perform= (anymethod,wrappers) => {
wrappers.forEach(wrap => { //wrappers是一个数组
wrap.initilizae();
});
anymethod();
wrappers.forEach(wrap => {
wrap.close();
});
}
perform(() => {
console.log('说话');
},[
{ // wrapper
initilizae(){
console.log('您好');
},
close() {
console.log('再见');
}
},
{ // wrapper2
initilizae(){
console.log('您好2');
},
close() {
console.log('再见2');
}
}
])
柯里化函数
柯里化: 就是将一个函数拆分成多个函数
高阶函数中包含 柯里化 可以保留参数
- 判断类型
Object.prototype.toString.call()
const checkType = (type,content) => {
return Object.prototype.toString.call(content) === `[object ${type}]`
}
console.log(checkType('String','123'));
判断类型实现
const checkType = (type) => {
return (content) => {
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
// 闭包
let types = ['Number','String','Boolean']
let utils = {} //工具类
types.forEach(type => {
utils['is' + type] = checkType(type)
})
console.log(utils.isString('123'));
- 函数柯里化怎么实现
通用的柯里化函数
const add = (a,b,c,d,e) => {
return a + b + c + d + e
}
const curring = (fn,arr = []) => { //空数组接收传递过来的参数合成新数组
let len = fn.length
return (...args) => { //args传进来的参数
arr = arr.concat(args);
if(arr.length < len) { //如果arr的长度小于函数参数的长度,则返回这个方法和arr继续等待传参
return curring(fn,arr)
}
return fn(...arr)
}
}
let r = curring(add)(1)(2)(3,4)(5)
console.log(r);
- 对判断类型的方法进行改造
const checkType = (type,content) => {
return Object.prototype.toString.call(content) === `[object ${type}]`
}
let types = ['Number','String','Boolean']
let utils = {}
types.forEach(type => {
utils['is' + type] = curring(checkType)(type) //这里使用上一步中的curring方法,先传入一个参数
})
console.log(utils.isString('123')); //再传入一个参数
after函数
after可以生成新的函数,等待函数执行达到我的预期时执行
const after = (times,fn) => {
return () => {
if(--times === 0) { //函数调用三次才会执行回调
fn();
}
}
}
let newAfter = after(3,() => {
console.log('三次后调用');
})
newAfter();
newAfter();
newAfter();
// lodash after
并发问题
我们希望 读取数据 node 异步 会等待同步代码都执行完成后再执行
node中,同步读取两个文件,由于不知道哪个先读取完,哪个后读取完,同步执行时代码未运行完成,存储对象school不能获取到读取的文件,如下:
const fs = require('fs');
let school = {}
// 并发的问题 如何解决 计数器
fs.readFile('name.txt','utf8',(err,data)=> {
school['name'] = data
})
fs.readFile('age.txt','utf8',(err,data)=> {
school['age'] = data
})
console.log(school); // {}
解决并发问题方法:
- 计数器
通过上面的after函数,定义执行次数,执行两次以后,读取school对象.
const after = (times,fn) => () => --times === 0 && fn();
let newAfter = after(2,() => {
console.log(school);
})
fs.readFile('name.txt','utf8',(err,data)=> {
school['name'] = data;
newAfter();
})
fs.readFile('age.txt','utf8',(err,data)=> {
school['age'] = data;
newAfter();
})
- 使用发布订阅模式
发布订阅模式
把订阅的事件存储到数组中可以订阅多个事件,只要发布者触发事件,执行函数,订阅者就会接收到
- 发布和订阅直接是没有关系的,订阅将要订阅的事件存储在一个空间中(数组),发布者在存储空间中发布事件.
const fs = require('fs');
let school = {}
let e = {
arr: [],
on(fn) {
this.arr.push(fn)
},
emit() {
this.arr.forEach(fn => fn())
}
}
e.on(() => { // 订阅
console.log('ok');
})
e.on(() => {
if(Object.keys(school).length ===2){
console.log(school);
}
})
fs.readFile('name.txt','utf8',(err,data)=> {
school['name'] = data;
e.emit(); //发布
})
fs.readFile('age.txt','utf8',(err,data)=> {
school['age'] = data;
e.emit();
})
订阅者订阅一个事件->只要school中的长度等于2,那么就输出数据.
发布者遍历事件数量,发布一次就将遍历的事件执行一次,供订阅者订阅.
观察者模式
观察者模式 基于 发布订阅 模式
例如:我和我媳妇要观察小宝宝,小宝宝一发生情绪变化就会立即通知给我和我媳妇他有情绪了.
class Sbuject { // 1.被观察者 小宝宝
constructor() {
this.arr = []; // 存储空间 [o1,o2]
this.state = '我很开心' // 被观察者的一个状态
}
attach(o) { // 3.接收观察者,存储在一个数组空间
this.arr.push(o)
}
setState(newState) { // 4.修改被观察者状态,通知所有观察者更新状态
this.state = newState
this.arr.forEach(o => o.updata(newState))
}
}
class Observer { // 1.观察者 我 我媳妇
constructor(name) {
this.name = name
}
updata(newState) { // 5.观察者订阅更新状态
console.log(this.name + '小宝宝' +newState);
}
}
// 2. 实例化
let s = new Sbuject('小宝宝'); //小宝宝
let o1 = new Observer('我');
let o2 = new Observer('我媳妇')
s.attach(o1); // 3.被观察者中添加观察者对象
s.attach(o2);
s.setState('不开心了') // 4.被观察者修改状态