函数式编程
函数式编程并不是说我们的程序所有的功能都具备下面的纯函数特征,而是规范一些公共函数的封装,就是比较考验我们的抽象能力,其可以让我们的程序更加简洁、易维护
ps:一般也就公共代码比较符合函数式编程,要是整个项目完全都按照下面特征根本不可能哈,我们的业务怎么写是吧,hook副作用函数就是一个鲜明例子😄
函数式变成主要有以下特征:
- 纯函数:函数的输出只取决于输入,避免副作用函数的出现(内部不会直接引入外部变量更新外部变量,例如:闭包场景),这样的纯函数更加可控,没有可怕的代码侵入副作用,代码也更加简洁、易维护
- 不可变性:数据一旦创建就不可变了,如果要改数据,那么需要创建一个新的变量,使用或返回新的变量,优点就是,当操作一个对象时,不会不小心修改传入对象而引起难以察觉的bug
- 函数组合:函数组合以减少重复代码的产生,例如:开发中比较常见的公共代码提取
- 高阶函数:函数的入参或者出参是函数指针,例如:函数柯里化、forEach、sort等函数
- 惰性计算,在必要的时候才计算执行函数,函数在第一次执行时,会用更高效的逻辑覆盖自身,仅第一次调用时有初始化/判断的开销,后续调用无额外成本,主要对于一些分支只会走一次场景的优化,例如:addEvent 根据不同版本的实现逻辑,单例+一次性初始化
这里面比较难理解的就是副作用和惰性计算了
副作用函数
就是函数体内部逻辑完全由入参决定,直接跟外部内容存在耦合
举一个简单的例子
js
let a = ...
//引用改变外部变量,对外部内容产生依赖,修改入参等
function handle(type) {
if (type) {
a.b = 'b'
} else {
a.b = ''
}
}
惰性计算
如下所示
js
//兼容代码处理,第一次根据环境变量判断,返回一个最终的高阶函数作为最终统一调用的函数
function addEvent(element, type, handler) {
// 第一次调用时,根据环境重写函数本身
if (element.addEventListener) {
// 重写:后续调用直接用 addEventListener
addEvent = function(element, type, handler) {
element.addEventListener(type, handler, false);
};
} else if (element.attachEvent) {
// 重写:后续调用直接用 attachEvent
addEvent = function(element, type, handler) {
element.attachEvent('on' + type, handler);
};
} else {
// 重写:后续调用直接用 onxxx
addEvent = function(element, type, handler) {
element['on' + type] = handler;
};
}
// 执行本次调用的逻辑(第一次调用时,先重写再执行)
return addEvent(element, type, handler);
}
function getConfig() {
// 第一次调用时初始化配置
const config = {
apiBaseUrl: 'https://api.example.com',
timeout: 5000,
token: localStorage.getItem('token')
};
// 重写函数:后续调用直接返回缓存的 config
getConfig = function() {
return config;
};
// 第一次调用返回初始化的 config
return config;
}
ps:由于执行时重塑函数,对于现在导出还是要注意引用指针的问题,可能不小心就是每次都是调用修改前的了,可能一般都是模块化导出,看似没问题,什么情况下会有问题呢(调用时,通过对象直接调用函数,不提前展开,猜猜为啥)😄
最后
提到了惰性函数就想到了懒加载,对于页面图片拉加载有哪些方式呢
ps:个人上来就想到了自定义变量 + IntersectionObserver;滚动时根据偏移手动观察dom是否在视野内,当然细节的需要自己处理了,好了就到这里了