HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

电影《疯狂动物城2》迅雷BT完整下载[HD-1280P/3.23GBMKV中字]4k百度云资源

分享

  《疯狂动物城2:在多元共生中寻找城市秩序的密码》
  当朱迪·霍普斯警官举着电磁脉冲武器冲进雨林,尼克·王尔德的西装暗纹在激光中泛着冷光,新角色蛇盖瑞的鳞片折射出沼泽市场的霓虹——2025年11月26日上映的《疯狂动物城2》,以一场跨越哺乳动物与爬行动物的冒险,将都市寓言推向更深刻的维度。这部暌违九年的续作,不仅延续了前作对偏见的解构,更以“城市温度”为切口,撕开了现代文明高速发展背后的集体焦虑。
  《疯狂动物城2》下载:点击前往
  一、叙事升级:从“消除偏见”到“重构共生”
  前作《疯狂动物城》以食草动物与食肉动物的二元对立为叙事支点,用“野蛮化”危机叩击种族平等的命题。续作则将矛盾升级为哺乳动物与爬行动物的族群冲突:记载气候墙原始设计图的古籍失窃,蛇盖瑞因被诬陷成为逃犯,朱迪与尼克在追捕过程中发现猞猁黑帮煽动对立的阴谋。这一设定直指现实中的“外来者困境”——当城市以效率为名加速扩张,那些被视为“缓慢”“冷血”的群体,是否注定成为被边缘化的代价?
  影片核心场景“沼泽市场”的构建极具象征意义。这个由爬行动物主导的地下世界,既有潮湿闷热的生存法则,又保留着独特的文化密码:蛇盖瑞用鳞片摩擦发出求救信号,蜥蜴摊贩用尾巴卷起交易货币,鳄鱼保安在泥潭中潜伏。当朱迪与尼克以“救赎联盟”身份潜入时,他们不得不摘下象征体制权威的警徽,学习用爬行动物的逻辑理解世界。这种文化碰撞的细节,比任何说教都更具冲击力——当朱迪因习惯直立行走而暴露行踪时,尼克调侃:“在沼泽里,低调才是生存之道。”
  二、角色弧光:在裂痕中生长的灵魂伴侣
  朱迪与尼克的关系进化是影片最动人的叙事线。这对曾以“不用说也懂”为默契的搭档,在续作中因办案理念分歧被迫接受心理辅导。导演拜伦·霍华德坦言:“当理想主义进入体制内,真正的考验才刚刚开始。”朱迪用过度责任感对抗失控焦虑,尼克以玩世不恭掩饰被驯化的恐惧,咨询室里的沉默与争执,精准击中了职场中处理复杂人际关系的成年人。
  这种“结构性疲惫”在“动物城百年庆典”危机中达到高潮。当猞猁黑帮煽动哺乳动物围攻气候墙,朱迪坚持按程序申请支援,尼克却擅自破坏控制系统启动应急方案。两人在暴雨中对峙的场景,将成年人的困境具象化:我们是否在追求“正确”的过程中,丢失了最珍贵的联结?最终,朱迪撕碎心理评估报告,尼克卸下西装领带,他们选择用最原始的方式信任彼此——朱迪跳上尼克后背,狐狸驮着兔子冲向火海。这个充满动物本能的姿势,恰恰是对“灵魂伴侣”最生动的诠释:明知不同,仍愿并肩。
  三、视听革新:技术赋能的沉浸式寓言
  影片在视觉呈现上实现了突破性升级。杜比影院版本中,气候墙的粒子特效以每秒120帧的流畅度展现,当朱迪穿越极地与沙漠的温差层时,观众能清晰感受到睫毛上的冰晶与汗水蒸发的热浪。4DX影厅的动感座椅则将追车戏推向新高度:尼克驾驶的改装沙地车碾过沼泽时,座椅会突然倾斜45度模拟侧翻,当车辆冲破水幕时,两侧喷出的水雾带着苔藓的腥气。这种多感官刺激,让“热力追踪”从银幕延伸至现实。
  音乐叙事同样充满巧思。夏奇拉演唱的主题曲《Zoo》融入哥伦比亚风笛与电子鼓点,歌词“我们用差异编织成网”呼应影片主题。更令人惊喜的是,上海迪士尼乐园将这首歌曲与前作《尝试一切》编排成夜间投影秀,当朱迪与尼克的剪影在城堡上共舞时,背景音乐突然切换成两人争吵的录音片段,随后渐变为和解时的轻笑——这种打破第四面墙的设计,让观众成为故事的一部分。
  四、现实映照:一堂给成年人的情绪整顿课
  影片最深刻的洞察,在于它撕开了都市生活的体面伪装。当朱迪在庆典现场看到慢吞吞的乌龟被推搡时,她突然意识到:自己何尝不是偏见施加者?那些被她视为“低效”的爬行动物,或许只是需要更多时间适应城市的节奏。这种自我反思,在现实中处处可见:我们抱怨地铁里的老人挡路,却忘记他们可能第一次使用扫码进站;我们指责外卖小哥超速,却忽略他们正在与系统算法赛跑。
  影片结尾的“双彩蛋”设计颇具深意:第一个彩蛋揭示气候墙古籍失窃案的幕后黑手竟是动物城档案局,他们为维护城市形象销毁历史证据;第二个彩蛋则展现朱迪与尼克在心理咨询室签订“冲突解决协议”,承诺“下次争吵时先数到十”。这两个看似矛盾的结局,恰恰构成了对现代社会的双重叩问:我们是否在追求秩序的过程中,扼杀了真实的生命力?而那些被视为“问题”的裂痕,是否正是光照进来的地方?
  结语:在动物城里,我们都是寻找秩序的旅人
  当片尾字幕升起,上海迪士尼乐园的“疯狂动物城:热力追踪”景点正上演着新角色蛇盖瑞的表演。孩子们为湿漉漉的爬行动物欢呼,大人们却在朱迪与尼克的拥抱中红了眼眶。这部动画电影最伟大的成就,或许在于它用最天真的方式,触碰了最沉重的命题:在这个日益分裂的世界里,我们是否还能像孩子一样,相信差异可以共生,裂痕能够愈合?
  正如影片中那句被反复引用的台词:“真正的城市,不是由玻璃与钢铁筑成,而是由理解与包容编织。”当朱迪与尼克驾驶着沙地车冲向晨曦时,他们背上的光芒,何尝不是每个都市人心中那团未灭的理想主义之火?

继续阅读 »

  《疯狂动物城2:在多元共生中寻找城市秩序的密码》
  当朱迪·霍普斯警官举着电磁脉冲武器冲进雨林,尼克·王尔德的西装暗纹在激光中泛着冷光,新角色蛇盖瑞的鳞片折射出沼泽市场的霓虹——2025年11月26日上映的《疯狂动物城2》,以一场跨越哺乳动物与爬行动物的冒险,将都市寓言推向更深刻的维度。这部暌违九年的续作,不仅延续了前作对偏见的解构,更以“城市温度”为切口,撕开了现代文明高速发展背后的集体焦虑。
  《疯狂动物城2》下载:点击前往
  一、叙事升级:从“消除偏见”到“重构共生”
  前作《疯狂动物城》以食草动物与食肉动物的二元对立为叙事支点,用“野蛮化”危机叩击种族平等的命题。续作则将矛盾升级为哺乳动物与爬行动物的族群冲突:记载气候墙原始设计图的古籍失窃,蛇盖瑞因被诬陷成为逃犯,朱迪与尼克在追捕过程中发现猞猁黑帮煽动对立的阴谋。这一设定直指现实中的“外来者困境”——当城市以效率为名加速扩张,那些被视为“缓慢”“冷血”的群体,是否注定成为被边缘化的代价?
  影片核心场景“沼泽市场”的构建极具象征意义。这个由爬行动物主导的地下世界,既有潮湿闷热的生存法则,又保留着独特的文化密码:蛇盖瑞用鳞片摩擦发出求救信号,蜥蜴摊贩用尾巴卷起交易货币,鳄鱼保安在泥潭中潜伏。当朱迪与尼克以“救赎联盟”身份潜入时,他们不得不摘下象征体制权威的警徽,学习用爬行动物的逻辑理解世界。这种文化碰撞的细节,比任何说教都更具冲击力——当朱迪因习惯直立行走而暴露行踪时,尼克调侃:“在沼泽里,低调才是生存之道。”
  二、角色弧光:在裂痕中生长的灵魂伴侣
  朱迪与尼克的关系进化是影片最动人的叙事线。这对曾以“不用说也懂”为默契的搭档,在续作中因办案理念分歧被迫接受心理辅导。导演拜伦·霍华德坦言:“当理想主义进入体制内,真正的考验才刚刚开始。”朱迪用过度责任感对抗失控焦虑,尼克以玩世不恭掩饰被驯化的恐惧,咨询室里的沉默与争执,精准击中了职场中处理复杂人际关系的成年人。
  这种“结构性疲惫”在“动物城百年庆典”危机中达到高潮。当猞猁黑帮煽动哺乳动物围攻气候墙,朱迪坚持按程序申请支援,尼克却擅自破坏控制系统启动应急方案。两人在暴雨中对峙的场景,将成年人的困境具象化:我们是否在追求“正确”的过程中,丢失了最珍贵的联结?最终,朱迪撕碎心理评估报告,尼克卸下西装领带,他们选择用最原始的方式信任彼此——朱迪跳上尼克后背,狐狸驮着兔子冲向火海。这个充满动物本能的姿势,恰恰是对“灵魂伴侣”最生动的诠释:明知不同,仍愿并肩。
  三、视听革新:技术赋能的沉浸式寓言
  影片在视觉呈现上实现了突破性升级。杜比影院版本中,气候墙的粒子特效以每秒120帧的流畅度展现,当朱迪穿越极地与沙漠的温差层时,观众能清晰感受到睫毛上的冰晶与汗水蒸发的热浪。4DX影厅的动感座椅则将追车戏推向新高度:尼克驾驶的改装沙地车碾过沼泽时,座椅会突然倾斜45度模拟侧翻,当车辆冲破水幕时,两侧喷出的水雾带着苔藓的腥气。这种多感官刺激,让“热力追踪”从银幕延伸至现实。
  音乐叙事同样充满巧思。夏奇拉演唱的主题曲《Zoo》融入哥伦比亚风笛与电子鼓点,歌词“我们用差异编织成网”呼应影片主题。更令人惊喜的是,上海迪士尼乐园将这首歌曲与前作《尝试一切》编排成夜间投影秀,当朱迪与尼克的剪影在城堡上共舞时,背景音乐突然切换成两人争吵的录音片段,随后渐变为和解时的轻笑——这种打破第四面墙的设计,让观众成为故事的一部分。
  四、现实映照:一堂给成年人的情绪整顿课
  影片最深刻的洞察,在于它撕开了都市生活的体面伪装。当朱迪在庆典现场看到慢吞吞的乌龟被推搡时,她突然意识到:自己何尝不是偏见施加者?那些被她视为“低效”的爬行动物,或许只是需要更多时间适应城市的节奏。这种自我反思,在现实中处处可见:我们抱怨地铁里的老人挡路,却忘记他们可能第一次使用扫码进站;我们指责外卖小哥超速,却忽略他们正在与系统算法赛跑。
  影片结尾的“双彩蛋”设计颇具深意:第一个彩蛋揭示气候墙古籍失窃案的幕后黑手竟是动物城档案局,他们为维护城市形象销毁历史证据;第二个彩蛋则展现朱迪与尼克在心理咨询室签订“冲突解决协议”,承诺“下次争吵时先数到十”。这两个看似矛盾的结局,恰恰构成了对现代社会的双重叩问:我们是否在追求秩序的过程中,扼杀了真实的生命力?而那些被视为“问题”的裂痕,是否正是光照进来的地方?
  结语:在动物城里,我们都是寻找秩序的旅人
  当片尾字幕升起,上海迪士尼乐园的“疯狂动物城:热力追踪”景点正上演着新角色蛇盖瑞的表演。孩子们为湿漉漉的爬行动物欢呼,大人们却在朱迪与尼克的拥抱中红了眼眶。这部动画电影最伟大的成就,或许在于它用最天真的方式,触碰了最沉重的命题:在这个日益分裂的世界里,我们是否还能像孩子一样,相信差异可以共生,裂痕能够愈合?
  正如影片中那句被反复引用的台词:“真正的城市,不是由玻璃与钢铁筑成,而是由理解与包容编织。”当朱迪与尼克驾驶着沙地车冲向晨曦时,他们背上的光芒,何尝不是每个都市人心中那团未灭的理想主义之火?

收起阅读 »

有企业证书的小伙伴可以建联一下吗?有几个问题请教,酬劳丰厚

外包 上架

公司有项目需要上传,绝对正规且合规,需要跟由企业证书的小伙伴咨询一下相关事宜,酬劳丰厚(不低于几个月工资)!!qq290783040 .不用留言,请直接加我。万分感谢本平台能够提供交流的空间。

公司有项目需要上传,绝对正规且合规,需要跟由企业证书的小伙伴咨询一下相关事宜,酬劳丰厚(不低于几个月工资)!!qq290783040 .不用留言,请直接加我。万分感谢本平台能够提供交流的空间。

寻找有企业证书的小伙伴咨询洽谈,酬劳丰厚!

App打包 iOS打包 uni_app 上架

公司有项目需要上传,绝对正规且合规,需要跟由企业证书的小伙伴咨询一下相关事宜,酬劳丰厚(不低于几个月工资)!!qq290783040 .不用留言,请直接加我。万分感谢本平台能够提供交流的空间。

公司有项目需要上传,绝对正规且合规,需要跟由企业证书的小伙伴咨询一下相关事宜,酬劳丰厚(不低于几个月工资)!!qq290783040 .不用留言,请直接加我。万分感谢本平台能够提供交流的空间。

安卓app中vue2版本vue文件套nvue开发地图,map的getScale方法无响应

this.$nextTick(()=>{
console.log('---scale----',this.$map.getScale)
this.$map.getScale({
success(res){
console.log('---scale----',res.scale)
that.mapScale=scale;
},
fail(err){
console.log('===',err)
},
complete(res){
console.log('==complete=',res)
}
})
});

继续阅读 »

this.$nextTick(()=>{
console.log('---scale----',this.$map.getScale)
this.$map.getScale({
success(res){
console.log('---scale----',res.scale)
that.mapScale=scale;
},
fail(err){
console.log('===',err)
},
complete(res){
console.log('==complete=',res)
}
})
});

收起阅读 »

【ios手机连接不上】使用爱思助手连接手机

真机运行 iOS iOS真机运行失败

如题,使用爱思助手连接手机!

目前我这边使用iTunes没反应,换了爱思助手连接就好了,可能爱思助手做了啥处理吧

如题,使用爱思助手连接手机!

目前我这边使用iTunes没反应,换了爱思助手连接就好了,可能爱思助手做了啥处理吧

uniapp.dcloud 插件市场 提现过期问题

插件市场 提现

钱包里的钱还能过期 ,就发个通知, 不满100又不能提现 ,也没有反馈渠道

钱包里的钱还能过期 ,就发个通知, 不满100又不能提现 ,也没有反馈渠道

底部面板可独立窗口

折叠面板

希望可以独立

希望可以独立

JavaScript 函数式编程完全指南

偏应用 柯里化 函数式编程

JavaScript 函数式编程完全指南

📚 目录

  1. 什么是函数式编程
  2. 纯函数
  3. 不可变性
  4. 高阶函数
  5. 核心数组方法
  6. 函数组合与管道
  7. 柯里化与偏应用
  8. 实战案例

1. 什么是函数式编程

函数式编程(Functional Programming,FP)是一种编程范式,它将计算视为数学函数的求值,强调:

  • 纯函数:相同输入总是产生相同输出
  • 不可变性:数据一旦创建就不能被修改
  • 函数组合:通过组合简单函数构建复杂功能
  • 避免副作用:函数不应该改变外部状态
// ❌ 命令式编程(告诉计算机"怎么做")  
const numbers = [1, 2, 3, 4, 5];  
const doubled = [];  
for (let i = 0; i < numbers.length; i++) {  
    doubled.push(numbers[i] * 2);  
}  
console.log(doubled); // [2, 4, 6, 8, 10]  

// ✅ 函数式编程(告诉计算机"做什么")  
const numbersFP = [1, 2, 3, 4, 5];  
const doubledFP = numbersFP.map(n => n * 2);  
console.log(doubledFP); // [2, 4, 6, 8, 10]

2. 纯函数

2.1 纯函数的定义

纯函数满足两个条件:

  1. 确定性:相同的输入永远返回相同的输出
  2. 无副作用:不修改外部状态,不依赖外部可变状态
// ✅ 纯函数示例  
function add(a, b) {  
    return a + b;  
}  

function multiply(a, b) {  
    return a * b;  
}  

function greet(name) {  
    return `Hello, ${name}!`;  
}  

// 多次调用,结果永远相同  
console.log(add(2, 3));      // 5  
console.log(add(2, 3));      // 5  
console.log(multiply(4, 5)); // 20  
console.log(greet('Alice')); // "Hello, Alice!"

2.2 非纯函数示例

// ❌ 非纯函数:依赖外部变量  
let taxRate = 0.1;  
function calculateTax(amount) {  
    return amount * taxRate; // 依赖外部变量 taxRate  
}  

console.log(calculateTax(100)); // 10  
taxRate = 0.2;  
console.log(calculateTax(100)); // 20 - 相同输入,不同输出!  

// ❌ 非纯函数:修改外部状态  
let total = 0;  
function addToTotal(value) {  
    total += value; // 副作用:修改了外部变量  
    return total;  
}  

console.log(addToTotal(5));  // 5  
console.log(addToTotal(5));  // 10 - 相同输入,不同输出!  

// ❌ 非纯函数:修改输入参数  
function addItem(cart, item) {  
    cart.push(item); // 副作用:修改了传入的数组  
    return cart;  
}  

const myCart = ['apple'];  
addItem(myCart, 'banana');  
console.log(myCart); // ['apple', 'banana'] - 原数组被修改了!

2.3 将非纯函数转换为纯函数

// ✅ 纯函数版本:将依赖作为参数传入  
function calculateTaxPure(amount, taxRate) {  
    return amount * taxRate;  
}  

console.log(calculateTaxPure(100, 0.1)); // 10  
console.log(calculateTaxPure(100, 0.2)); // 20  

// ✅ 纯函数版本:返回新值而不是修改外部状态  
function addToTotalPure(currentTotal, value) {  
    return currentTotal + value;  
}  

let totalPure = 0;  
totalPure = addToTotalPure(totalPure, 5);  
console.log(totalPure); // 5  

// ✅ 纯函数版本:返回新数组而不是修改原数组  
function addItemPure(cart, item) {  
    return [...cart, item]; // 返回新数组  
}  

const myCartPure = ['apple'];  
const newCart = addItemPure(myCartPure, 'banana');  
console.log(myCartPure); // ['apple'] - 原数组未被修改  
console.log(newCart);    // ['apple', 'banana']

2.4 纯函数的好处

// 1. 可测试性 - 纯函数非常容易测试  
function calculateDiscount(price, discountPercent) {  
    return price * (1 - discountPercent / 100);  
}  

// 测试用例  
console.log(calculateDiscount(100, 20) === 80);  // true  
console.log(calculateDiscount(50, 10) === 45);   // true  
console.log(calculateDiscount(200, 50) === 100); // true  

// 2. 可缓存性 - 相同输入总是相同输出,可以缓存结果  
function memoize(fn) {  
    const cache = new Map();  
    return function(...args) {  
        const key = JSON.stringify(args);  
        if (cache.has(key)) {  
            console.log('从缓存获取');  
            return cache.get(key);  
        }  
        console.log('计算中...');  
        const result = fn(...args);  
        cache.set(key, result);  
        return result;  
    };  
}  

function expensiveCalculation(n) {  
    // 模拟耗时计算  
    let result = 0;  
    for (let i = 0; i < n; i++) {  
        result += i;  
    }  
    return result;  
}  

const memoizedCalc = memoize(expensiveCalculation);  
console.log(memoizedCalc(10000)); // 计算中... 49995000  
console.log(memoizedCalc(10000)); // 从缓存获取 49995000  
console.log(memoizedCalc(5000));  // 计算中... 12497500

3. 不可变性

3.1 什么是不可变性

不可变性意味着数据一旦创建,就不能被修改。任何"修改"操作都会返回新的数据。

// ❌ 可变操作  
const person = { name: 'Alice', age: 25 };  
person.age = 26; // 直接修改原对象  
console.log(person); // { name: 'Alice', age: 26 }  

// ✅ 不可变操作  
const personImmutable = { name: 'Alice', age: 25 };  
const updatedPerson = { ...personImmutable, age: 26 }; // 创建新对象  
console.log(personImmutable); // { name: 'Alice', age: 25 } - 原对象不变  
console.log(updatedPerson);   // { name: 'Alice', age: 26 }

3.2 数组的不可变操作

const fruits = ['apple', 'banana', 'orange'];  

// ❌ 可变方法(会修改原数组)  
// push, pop, shift, unshift, splice, sort, reverse  

// ✅ 不可变方法(返回新数组)  
// map, filter, reduce, concat, slice, spread operator  

// 添加元素  
const withGrape = [...fruits, 'grape'];  
console.log(fruits);    // ['apple', 'banana', 'orange']  
console.log(withGrape); // ['apple', 'banana', 'orange', 'grape']  

// 在开头添加  
const withMango = ['mango', ...fruits];  
console.log(withMango); // ['mango', 'apple', 'banana', 'orange']  

// 删除元素(通过 filter)  
const withoutBanana = fruits.filter(f => f !== 'banana');  
console.log(withoutBanana); // ['apple', 'orange']  

// 修改元素(通过 map)  
const upperFruits = fruits.map(f => f.toUpperCase());  
console.log(upperFruits); // ['APPLE', 'BANANA', 'ORANGE']  

// 在指定位置插入  
const insertAt = (arr, index, item) => [  
    ...arr.slice(0, index),  
    item,  
    ...arr.slice(index)  
];  
console.log(insertAt(fruits, 1, 'kiwi')); // ['apple', 'kiwi', 'banana', 'orange']  

// 删除指定位置的元素  
const removeAt = (arr, index) => [  
    ...arr.slice(0, index),  
    ...arr.slice(index + 1)  
];  
console.log(removeAt(fruits, 1)); // ['apple', 'orange']  

// 更新指定位置的元素  
const updateAt = (arr, index, newValue) =>   
    arr.map((item, i) => i === index ? newValue : item);  
console.log(updateAt(fruits, 1, 'blueberry')); // ['apple', 'blueberry', 'orange']

3.3 对象的不可变操作

const user = {  
    name: 'Alice',  
    age: 25,  
    address: {  
        city: 'Beijing',  
        country: 'China'  
    },  
    hobbies: ['reading', 'coding']  
};  

// 更新顶层属性  
const userWithNewAge = { ...user, age: 26 };  
console.log(user.age);            // 25  
console.log(userWithNewAge.age);  // 26  

// 添加新属性  
const userWithEmail = { ...user, email: 'alice@example.com' };  
console.log(userWithEmail);  

// 删除属性  
const { age, ...userWithoutAge } = user;  
console.log(userWithoutAge); // { name: 'Alice', address: {...}, hobbies: [...] }  

// 更新嵌套属性(需要深层展开)  
const userWithNewCity = {  
    ...user,  
    address: {  
        ...user.address,  
        city: 'Shanghai'  
    }  
};  
console.log(user.address.city);           // 'Beijing'  
console.log(userWithNewCity.address.city); // 'Shanghai'  

// 更新数组属性  
const userWithNewHobby = {  
    ...user,  
    hobbies: [...user.hobbies, 'gaming']  
};  
console.log(user.hobbies);           // ['reading', 'coding']  
console.log(userWithNewHobby.hobbies); // ['reading', 'coding', 'gaming']

3.4 深度不可变更新工具函数

// 通用的深度更新函数  
function updatePath(obj, path, value) {  
    const keys = path.split('.');  

    if (keys.length === 1) {  
        return { ...obj, [keys[0]]: value };  
    }  

    const [first, ...rest] = keys;  
    return {  
        ...obj,  
        [first]: updatePath(obj[first], rest.join('.'), value)  
    };  
}  

const state = {  
    user: {  
        profile: {  
            name: 'Alice',  
            settings: {  
                theme: 'dark',  
                language: 'en'  
            }  
        }  
    }  
};  

const newState = updatePath(state, 'user.profile.settings.theme', 'light');  
console.log(state.user.profile.settings.theme);    // 'dark'  
console.log(newState.user.profile.settings.theme); // 'light'  

// 使用 Object.freeze 强制不可变(浅层)  
const frozenObj = Object.freeze({ a: 1, b: 2 });  
// frozenObj.a = 100; // 严格模式下会报错,非严格模式静默失败  
console.log(frozenObj.a); // 1  

// 深度冻结  
function deepFreeze(obj) {  
    Object.keys(obj).forEach(key => {  
        if (typeof obj[key] === 'object' && obj[key] !== null) {  
            deepFreeze(obj[key]);  
        }  
    });  
    return Object.freeze(obj);  
}  

const deepFrozenObj = deepFreeze({  
    a: 1,  
    b: { c: 2, d: { e: 3 } }  
});

4. 高阶函数

4.1 什么是高阶函数

高阶函数是至少满足以下条件之一的函数:

  1. 接受函数作为参数
  2. 返回一个函数
// 接受函数作为参数  
function executeOperation(a, b, operation) {  
    return operation(a, b);  
}  

const add = (x, y) => x + y;  
const multiply = (x, y) => x * y;  
const subtract = (x, y) => x - y;  

console.log(executeOperation(5, 3, add));      // 8  
console.log(executeOperation(5, 3, multiply)); // 15  
console.log(executeOperation(5, 3, subtract)); // 2  

// 返回一个函数  
function createMultiplier(factor) {  
    return function(number) {  
        return number * factor;  
    };  
}  

const double = createMultiplier(2);  
const triple = createMultiplier(3);  
const tenTimes = createMultiplier(10);  

console.log(double(5));   // 10  
console.log(triple(5));   // 15  
console.log(tenTimes(5)); // 50

4.2 常用高阶函数模式

// 1. 函数包装器 - 添加额外功能  
function withLogging(fn) {  
    return function(...args) {  
        console.log(`调用函数,参数: ${JSON.stringify(args)}`);  
        const result = fn(...args);  
        console.log(`返回结果: ${result}`);  
        return result;  
    };  
}  

function addNumbers(a, b) {  
    return a + b;  
}  

const addWithLogging = withLogging(addNumbers);  
addWithLogging(3, 4);  
// 输出:  
// 调用函数,参数: [3,4]  
// 返回结果: 7  

// 2. 函数计时器  
function withTiming(fn) {  
    return function(...args) {  
        const start = performance.now();  
        const result = fn(...args);  
        const end = performance.now();  
        console.log(`执行时间: ${(end - start).toFixed(2)}ms`);  
        return result;  
    };  
}  

function slowFunction() {  
    let sum = 0;  
    for (let i = 0; i < 1000000; i++) {  
        sum += i;  
    }  
    return sum;  
}  

const timedSlowFunction = withTiming(slowFunction);  
timedSlowFunction(); // 执行时间: x.xxms  

// 3. 只执行一次的函数  
function once(fn) {  
    let called = false;  
    let result;  

    return function(...args) {  
        if (!called) {  
            called = true;  
            result = fn(...args);  
        }  
        return result;  
    };  
}  

const initialize = once(() => {  
    console.log('初始化中...');  
    return { initialized: true };  
});  

console.log(initialize()); // 初始化中... { initialized: true }  
console.log(initialize()); // { initialized: true } - 不再打印"初始化中..."  
console.log(initialize()); // { initialized: true }  

// 4. 防抖函数  
function debounce(fn, delay) {  
    let timeoutId;  

    return function(...args) {  
        clearTimeout(timeoutId);  
        timeoutId = setTimeout(() => {  
            fn.apply(this, args);  
        }, delay);  
    };  
}  

const debouncedSearch = debounce((query) => {  
    console.log(`搜索: ${query}`);  
}, 300);  

// 快速连续调用,只有最后一次会执行  
debouncedSearch('a');  
debouncedSearch('ab');  
debouncedSearch('abc');  
// 300ms 后只输出: 搜索: abc  

// 5. 节流函数  
function throttle(fn, limit) {  
    let inThrottle = false;  

    return function(...args) {  
        if (!inThrottle) {  
            fn.apply(this, args);  
            inThrottle = true;  
            setTimeout(() => {  
                inThrottle = false;  
            }, limit);  
        }  
    };  
}  

const throttledScroll = throttle(() => {  
    console.log('处理滚动事件');  
}, 100);

4.3 创建专用函数

// 使用高阶函数创建专用函数  
function createValidator(validationFn, errorMessage) {  
    return function(value) {  
        if (validationFn(value)) {  
            return { valid: true, value };  
        }  
        return { valid: false, error: errorMessage };  
    };  
}  

const isNotEmpty = createValidator(  
    value => value && value.trim().length > 0,  
    '值不能为空'  
);  

const isEmail = createValidator(  
    value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),  
    '请输入有效的邮箱地址'  
);  

const isMinLength = (min) => createValidator(  
    value => value && value.length >= min,  
    `长度至少为 ${min} 个字符`  
);  

console.log(isNotEmpty('hello'));     // { valid: true, value: 'hello' }  
console.log(isNotEmpty(''));          // { valid: false, error: '值不能为空' }  
console.log(isEmail('test@mail.com')); // { valid: true, value: 'test@mail.com' }  
console.log(isEmail('invalid'));       // { valid: false, error: '请输入有效的邮箱地址' }  

const isMinLength5 = isMinLength(5);  
console.log(isMinLength5('hello'));    // { valid: true, value: 'hello' }  
console.log(isMinLength5('hi'));       // { valid: false, error: '长度至少为 5 个字符' }

5. 核心数组方法

5.1 map - 转换每个元素

// map 的基本用法  
const numbers = [1, 2, 3, 4, 5];  

// 简单转换  
const doubled = numbers.map(n => n * 2);  
console.log(doubled); // [2, 4, 6, 8, 10]  

const squared = numbers.map(n => n ** 2);  
console.log(squared); // [1, 4, 9, 16, 25]  

// 转换对象数组  
const users = [  
    { name: 'Alice', age: 25 },  
    { name: 'Bob', age: 30 },  
    { name: 'Charlie', age: 35 }  
];  

const names = users.map(user => user.name);  
console.log(names); // ['Alice', 'Bob', 'Charlie']  

const userCards = users.map(user => ({  
    ...user,  
    displayName: `${user.name} (${user.age}岁)`  
}));  
console.log(userCards);  
// [  
//   { name: 'Alice', age: 25, displayName: 'Alice (25岁)' },  
//   { name: 'Bob', age: 30, displayName: 'Bob (30岁)' },  
//   { name: 'Charlie', age: 35, displayName: 'Charlie (35岁)' }  
// ]  

// 使用索引参数  
const indexed = numbers.map((n, index) => `${index}: ${n}`);  
console.log(indexed); // ['0: 1', '1: 2', '2: 3', '3: 4', '4: 5']  

// 自己实现 map  
function myMap(arr, fn) {  
    const result = [];  
    for (let i = 0; i < arr.length; i++) {  
        result.push(fn(arr[i], i, arr));  
    }  
    return result;  
}  

console.log(myMap([1, 2, 3], x => x * 10)); // [10, 20, 30]

5.2 filter - 过滤元素

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  

// 基本过滤  
const evens = numbers.filter(n => n % 2 === 0);  
console.log(evens); // [2, 4, 6, 8, 10]  

const odds = numbers.filter(n => n % 2 !== 0);  
console.log(odds); // [1, 3, 5, 7, 9]  

const greaterThan5 = numbers.filter(n => n > 5);  
console.log(greaterThan5); // [6, 7, 8, 9, 10]  

// 过滤对象数组  
const products = [  
    { name: 'iPhone', price: 999, inStock: true },  
    { name: 'iPad', price: 799, inStock: false },  
    { name: 'MacBook', price: 1299, inStock: true },  
    { name: 'AirPods', price: 199, inStock: true }  
];  

const inStockProducts = products.filter(p => p.inStock);  
console.log(inStockProducts);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'MacBook', price: 1299, inStock: true },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

const affordableProducts = products.filter(p => p.price < 1000);  
console.log(affordableProducts);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'iPad', price: 799, inStock: false },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

// 组合条件  
const affordableInStock = products.filter(p => p.price < 1000 && p.inStock);  
console.log(affordableInStock);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

// 去除假值  
const mixedArray = [0, 1, '', 'hello', null, undefined, false, true, NaN];  
const truthyValues = mixedArray.filter(Boolean);  
console.log(truthyValues); // [1, 'hello', true]  

// 去重  
const duplicates = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];  
const unique = duplicates.filter((item, index, arr) => arr.indexOf(item) === index);  
console.log(unique); // [1, 2, 3, 4]  

// 自己实现 filter  
function myFilter(arr, predicate) {  
    const result = [];  
    for (let i = 0; i < arr.length; i++) {  
        if (predicate(arr[i], i, arr)) {  
            result.push(arr[i]);  
        }  
    }  
    return result;  
}  

console.log(myFilter([1, 2, 3, 4, 5], x => x > 2)); // [3, 4, 5]

5.3 reduce - 归约为单一值

// 基本用法:求和  
const numbers = [1, 2, 3, 4, 5];  

const sum = numbers.reduce((accumulator, current) => {  
    console.log(`accumulator: ${accumulator}, current: ${current}`);  
    return accumulator + current;  
}, 0);  
// accumulator: 0, current: 1  
// accumulator: 1, current: 2  
// accumulator: 3, current: 3  
// accumulator: 6, current: 4  
// accumulator: 10, current: 5  
console.log(sum); // 15  

// 求乘积  
const product = numbers.reduce((acc, cur) => acc * cur, 1);  
console.log(product); // 120  

// 找最大值  
const max = numbers.reduce((acc, cur) => cur > acc ? cur : acc, -Infinity);  
console.log(max); // 5  

// 找最小值  
const min = numbers.reduce((acc, cur) => cur < acc ? cur : acc, Infinity);  
console.log(min); // 1  

// 数组转对象  
const users = [  
    { id: 1, name: 'Alice' },  
    { id: 2, name: 'Bob' },  
    { id: 3, name: 'Charlie' }  
];  

const userMap = users.reduce((acc, user) => {  
    acc[user.id] = user;  
    return acc;  
}, {});  
console.log(userMap);  
// {  
//   1: { id: 1, name: 'Alice' },  
//   2: { id: 2, name: 'Bob' },  
//   3: { id: 3, name: 'Charlie' }  
// }  

// 分组  
const people = [  
    { name: 'Alice', age: 25, city: 'Beijing' },  
    { name: 'Bob', age: 30, city: 'Shanghai' },  
    { name: 'Charlie', age: 25, city: 'Beijing' },  
    { name: 'David', age: 30, city: 'Beijing' }  
];  

const groupByAge = people.reduce((acc, person) => {  
    const key = person.age;  
    if (!acc[key]) {  
        acc[key] = [];  
    }  
    acc[key].push(person);  
    return acc;  
}, {});  
console.log(groupByAge);  
// {  
//   25: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],  
//   30: [{ name: 'Bob', ... }, { name: 'David', ... }]  
// }  

// 通用分组函数  
function groupBy(arr, keyFn) {  
    return arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        if (!acc[key]) {  
            acc[key] = [];  
        }  
        acc[key].push(item);  
        return acc;  
    }, {});  
}  

console.log(groupBy(people, p => p.city));  
// {  
//   Beijing: [Alice, Charlie, David],  
//   Shanghai: [Bob]  
// }  

// 统计出现次数  
const words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];  

const wordCount = words.reduce((acc, word) => {  
    acc[word] = (acc[word] || 0) + 1;  
    return acc;  
}, {});  
console.log(wordCount); // { apple: 3, banana: 2, orange: 1 }  

// 扁平化数组  
const nested = [[1, 2], [3, 4], [5, 6]];  
const flattened = nested.reduce((acc, arr) => [...acc, ...arr], []);  
console.log(flattened); // [1, 2, 3, 4, 5, 6]  

// 深度扁平化  
const deepNested = [[1, [2, 3]], [4, [5, [6, 7]]]];  

function flatten(arr) {  
    return arr.reduce((acc, item) => {  
        if (Array.isArray(item)) {  
            return [...acc, ...flatten(item)];  
        }  
        return [...acc, item];  
    }, []);  
}  

console.log(flatten(deepNested)); // [1, 2, 3, 4, 5, 6, 7]  

// 使用 reduce 实现 map  
function mapWithReduce(arr, fn) {  
    return arr.reduce((acc, item, index) => {  
        acc.push(fn(item, index, arr));  
        return acc;  
    }, []);  
}  

console.log(mapWithReduce([1, 2, 3], x => x * 2)); // [2, 4, 6]  

// 使用 reduce 实现 filter  
function filterWithReduce(arr, predicate) {  
    return arr.reduce((acc, item, index) => {  
        if (predicate(item, index, arr)) {  
            acc.push(item);  
        }  
        return acc;  
    }, []);  
}  

console.log(filterWithReduce([1, 2, 3, 4, 5], x => x > 2)); // [3, 4, 5]  

// 自己实现 reduce  
function myReduce(arr, reducer, initialValue) {  
    let accumulator = initialValue;  
    let startIndex = 0;  

    if (arguments.length < 3) {  
        accumulator = arr[0];  
        startIndex = 1;  
    }  

    for (let i = startIndex; i < arr.length; i++) {  
        accumulator = reducer(accumulator, arr[i], i, arr);  
    }  

    return accumulator;  
}  

console.log(myReduce([1, 2, 3, 4, 5], (a, b) => a + b, 0)); // 15

5.4 其他有用的数组方法

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  

// find - 找到第一个满足条件的元素  
const firstEven = numbers.find(n => n % 2 === 0);  
console.log(firstEven); // 2  

const firstGreaterThan5 = numbers.find(n => n > 5);  
console.log(firstGreaterThan5); // 6  

// findIndex - 找到第一个满足条件的元素的索引  
const firstEvenIndex = numbers.findIndex(n => n % 2 === 0);  
console.log(firstEvenIndex); // 1  

// some - 检查是否至少有一个元素满足条件  
const hasEven = numbers.some(n => n % 2 === 0);  
console.log(hasEven); // true  

const hasNegative = numbers.some(n => n < 0);  
console.log(hasNegative); // false  

// every - 检查是否所有元素都满足条件  
const allPositive = numbers.every(n => n > 0);  
console.log(allPositive); // true  

const allEven = numbers.every(n => n % 2 === 0);  
console.log(allEven); // false  

// includes - 检查数组是否包含某个值  
console.log(numbers.includes(5));  // true  
console.log(numbers.includes(11)); // false  

// flat - 扁平化数组  
const nestedArray = [1, [2, 3], [4, [5, 6]]];  
console.log(nestedArray.flat());    // [1, 2, 3, 4, [5, 6]]  
console.log(nestedArray.flat(2));   // [1, 2, 3, 4, 5, 6]  
console.log(nestedArray.flat(Infinity)); // [1, 2, 3, 4, 5, 6]  

// flatMap - map + flat(1)  
const sentences = ['Hello World', 'Goodbye World'];  
const words = sentences.flatMap(s => s.split(' '));  
console.log(words); // ['Hello', 'World', 'Goodbye', 'World']  

// 实用示例:处理可能返回数组的映射  
const data = [1, 2, 3];  
const duplicated = data.flatMap(n => [n, n]);  
console.log(duplicated); // [1, 1, 2, 2, 3, 3]

5.5 方法链式调用

const orders = [  
    { id: 1, customer: 'Alice', items: ['apple', 'banana'], total: 25, status: 'completed' },  
    { id: 2, customer: 'Bob', items: ['orange'], total: 15, status: 'pending' },  
    { id: 3, customer: 'Charlie', items: ['apple', 'grape', 'melon'], total: 45, status: 'completed' },  
    { id: 4, customer: 'David', items: ['banana'], total: 10, status: 'cancelled' },  
    { id: 5, customer: 'Eve', items: ['apple', 'orange'], total: 30, status: 'completed' }  
];  

// 链式调用:筛选已完成订单,计算平均订单金额  
const averageCompletedOrderValue = orders  
    .filter(order => order.status === 'completed')  
    .map(order => order.total)  
    .reduce((sum, total, _, arr) => sum + total / arr.length, 0);  

console.log(averageCompletedOrderValue); // 33.33...  

// 获取所有完成订单的商品列表(去重)  
const completedOrderItems = orders  
    .filter(order => order.status === 'completed')  
    .flatMap(order => order.items)  
    .filter((item, index, arr) => arr.indexOf(item) === index);  

console.log(completedOrderItems); // ['apple', 'banana', 'grape', 'melon', 'orange']  

// 创建订单摘要  
const orderSummary = orders  
    .filter(order => order.status !== 'cancelled')  
    .map(order => ({  
        orderId: order.id,  
        customer: order.customer,  
        itemCount: order.items.length,  
        total: order.total  
    }))  
    .sort((a, b) => b.total - a.total);  

console.log(orderSummary);  
// [  
//   { orderId: 3, customer: 'Charlie', itemCount: 3, total: 45 },  
//   { orderId: 5, customer: 'Eve', itemCount: 2, total: 30 },  
//   { orderId: 1, customer: 'Alice', itemCount: 2, total: 25 },  
//   { orderId: 2, customer: 'Bob', itemCount: 1, total: 15 }  
// ]  

// 按状态分组统计  
const statusStats = orders.reduce((acc, order) => {  
    if (!acc[order.status]) {  
        acc[order.status] = { count: 0, totalValue: 0 };  
    }  
    acc[order.status].count++;  
    acc[order.status].totalValue += order.total;  
    return acc;  
}, {});  

console.log(statusStats);  
// {  
//   completed: { count: 3, totalValue: 100 },  
//   pending: { count: 1, totalValue: 15 },  
//   cancelled: { count: 1, totalValue: 10 }  
// }

6. 函数组合与管道

6.1 函数组合 (Compose)

函数组合是将多个函数合并成一个函数,从右到左执行。

// 基本概念  
// compose(f, g, h)(x) 等价于 f(g(h(x)))  

// 简单的两个函数组合  
const compose2 = (f, g) => x => f(g(x));  

const addOne = x => x + 1;  
const double = x => x * 2;  

const addOneThenDouble = compose2(double, addOne); // 先加1,再乘2  
console.log(addOneThenDouble(5)); // (5 + 1) * 2 = 12  

const doubleThenAddOne = compose2(addOne, double); // 先乘2,再加1  
console.log(doubleThenAddOne(5)); // (5 * 2) + 1 = 11  

// 通用的 compose 函数(支持多个函数)  
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);  

const square = x => x ** 2;  
const negate = x => -x;  

const composed = compose(negate, square, addOne, double);  
// 执行顺序: double -> addOne -> square -> negate  
// 5 -> 10 -> 11 -> 121 -> -121  
console.log(composed(5)); // -121  

// 更复杂的例子:处理字符串  
const trim = str => str.trim();  
const toLowerCase = str => str.toLowerCase();  
const split = delimiter => str => str.split(delimiter);  
const join = delimiter => arr => arr.join(delimiter);  
const map = fn => arr => arr.map(fn);  
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);  

const slugify = compose(  
    join('-'),  
    map(toLowerCase),  
    split(' '),  
    trim  
);  

console.log(slugify('  Hello World  ')); // 'hello-world'  
console.log(slugify('JavaScript Is Awesome')); // 'javascript-is-awesome'

6.2 管道 (Pipe)

管道与组合类似,但从左到右执行,更符合阅读习惯。

// pipe(f, g, h)(x) 等价于 h(g(f(x)))  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  

const addOne = x => x + 1;  
const double = x => x * 2;  
const square = x => x ** 2;  

const piped = pipe(double, addOne, square);  
// 执行顺序: double -> addOne -> square  
// 5 -> 10 -> 11 -> 121  
console.log(piped(5)); // 121  

// 实际应用示例:数据处理管道  
const users = [  
    { name: 'alice', age: 25, role: 'admin' },  
    { name: 'bob', age: 30, role: 'user' },  
    { name: 'charlie', age: 35, role: 'admin' },  
    { name: 'david', age: 28, role: 'user' }  
];  

// 辅助函数  
const filter = predicate => arr => arr.filter(predicate);  
const map = fn => arr => arr.map(fn);  
const sortBy = key => arr => [...arr].sort((a, b) => a[key] - b[key]);  
const take = n => arr => arr.slice(0, n);  

// 构建处理管道  
const getTopAdminNames = pipe(  
    filter(u => u.role === 'admin'),  // 筛选管理员  
    sortBy('age'),                      // 按年龄排序  
    map(u => u.name.toUpperCase()),    // 获取大写名字  
    take(2)                             // 取前两个  
);  

console.log(getTopAdminNames(users)); // ['ALICE', 'CHARLIE']

6.3 异步管道

// 支持异步函数的管道  
const pipeAsync = (...fns) => initialValue =>  
    fns.reduce(  
        (promise, fn) => promise.then(fn),  
        Promise.resolve(initialValue)  
    );  

// 模拟异步操作  
const fetchUser = async (id) => {  
    console.log(`获取用户 ${id}...`);  
    return { id, name: 'Alice', email: 'alice@example.com' };  
};  

const fetchUserPosts = async (user) => {  
    console.log(`获取 ${user.name} 的帖子...`);  
    return {  
        ...user,  
        posts: ['Post 1', 'Post 2', 'Post 3']  
    };  
};  

const formatUserData = async (data) => {  
    console.log('格式化数据...');  
    return {  
        displayName: data.name.toUpperCase(),  
        email: data.email,  
        postCount: data.posts.length  
    };  
};  

const processUser = pipeAsync(  
    fetchUser,  
    fetchUserPosts,  
    formatUserData  
);  

processUser(1).then(console.log);  
// 获取用户 1...  
// 获取 Alice 的帖子...  
// 格式化数据...  
// { displayName: 'ALICE', email: 'alice@example.com', postCount: 3 }

6.4 条件组合

// 创建条件执行函数  
const when = (predicate, fn) => x => predicate(x) ? fn(x) : x;  

const unless = (predicate, fn) => x => predicate(x) ? x : fn(x);  

const isEven = x => x % 2 === 0;  
const double = x => x * 2;  
const addOne = x => x + 1;  

const doubleIfEven = when(isEven, double);  
console.log(doubleIfEven(4)); // 8  
console.log(doubleIfEven(5)); // 5  

const addOneIfOdd = unless(isEven, addOne);  
console.log(addOneIfOdd(4)); // 4  
console.log(addOneIfOdd(5)); // 6  

// 分支组合  
const ifElse = (predicate, onTrue, onFalse) => x =>  
    predicate(x) ? onTrue(x) : onFalse(x);  

const processNumber = ifElse(  
    isEven,  
    x => `${x} 是偶数,乘2得 ${x * 2}`,  
    x => `${x} 是奇数,加1得 ${x + 1}`  
);  

console.log(processNumber(4)); // "4 是偶数,乘2得 8"  
console.log(processNumber(5)); // "5 是奇数,加1得 6"  

// 多条件分支  
const cond = (...pairs) => x => {  
    for (const [predicate, fn] of pairs) {  
        if (predicate(x)) {  
            return fn(x);  
        }  
    }  
    return x;  
};  

const classifyAge = cond(  
    [age => age < 13, () => '儿童'],  
    [age => age < 20, () => '青少年'],  
    [age => age < 60, () => '成年人'],  
    [() => true, () => '老年人']  
);  

console.log(classifyAge(8));  // "儿童"  
console.log(classifyAge(15)); // "青少年"  
console.log(classifyAge(30)); // "成年人"  
console.log(classifyAge(70)); // "老年人"

7. 柯里化与偏应用

7.1 柯里化 (Currying)

柯里化是将一个接受多个参数的函数转换为一系列接受单个参数的函数。

// 普通函数  
function add(a, b, c) {  
    return a + b + c;  
}  
console.log(add(1, 2, 3)); // 6  

// 手动柯里化  
function addCurried(a) {  
    return function(b) {  
        return function(c) {  
            return a + b + c;  
        };  
    };  
}  
console.log(addCurried(1)(2)(3)); // 6  

// 箭头函数版本  
const addCurriedArrow = a => b => c => a + b + c;  
console.log(addCurriedArrow(1)(2)(3)); // 6  

// 部分应用  
const add1 = addCurriedArrow(1);  
const add1and2 = add1(2);  
console.log(add1and2(3)); // 6  
console.log(add1and2(10)); // 13  

// 通用柯里化函数  
function curry(fn) {  
    return function curried(...args) {  
        if (args.length >= fn.length) {  
            return fn.apply(this, args);  
        }  
        return function(...moreArgs) {  
            return curried.apply(this, [...args, ...moreArgs]);  
        };  
    };  
}  

// 使用示例  
function multiply(a, b, c) {  
    return a * b * c;  
}  

const curriedMultiply = curry(multiply);  

console.log(curriedMultiply(2)(3)(4));     // 24  
console.log(curriedMultiply(2, 3)(4));     // 24  
console.log(curriedMultiply(2)(3, 4));     // 24  
console.log(curriedMultiply(2, 3, 4));     // 24  

// 创建专用函数  
const double = curriedMultiply(2)(1);  
console.log(double(5));  // 10  
console.log(double(10)); // 20

7.2 柯里化的实际应用

// 1. 配置化的API请求  
const request = curry((method, baseUrl, endpoint, data) => {  
    console.log(`${method} ${baseUrl}${endpoint}`, data);  
    // 实际实现会发送真实请求  
    return { method, url: `${baseUrl}${endpoint}`, data };  
});  

const apiRequest = request('POST')('https://api.example.com');  
const createUser = apiRequest('/users');  
const createPost = apiRequest('/posts');  

createUser({ name: 'Alice' }); // POST https://api.example.com/users { name: 'Alice' }  
createPost({ title: 'Hello' }); // POST https://api.example.com/posts { title: 'Hello' }  

// 2. 格式化函数  
const formatCurrency = curry((symbol, decimals, amount) => {  
    return `${symbol}${amount.toFixed(decimals)}`;  
});  

const formatUSD = formatCurrency('$')(2);  
const formatEUR = formatCurrency('€')(2);  
const formatJPY = formatCurrency('¥')(0);  

console.log(formatUSD(1234.5));   // "$1234.50"  
console.log(formatEUR(1234.5));   // "€1234.50"  
console.log(formatJPY(1234.5));   // "¥1235"  

// 3. 事件处理  
const handleEvent = curry((handler, eventName, element) => {  
    element.addEventListener(eventName, handler);  
    return () => element.removeEventListener(eventName, handler);  
});  

const logEvent = event => console.log('Event:', event.type);  
const addClickHandler = handleEvent(logEvent)('click');  

// addClickHandler(document.getElementById('myButton'));  

// 4. 验证函数  
const validate = curry((validator, errorMsg, value) => {  
    return validator(value)   
        ? { valid: true, value }  
        : { valid: false, error: errorMsg };  
});  

const isNotEmpty = value => value && value.trim().length > 0;  
const isEmail = value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);  
const minLength = min => value => value && value.length >= min;  

const validateRequired = validate(isNotEmpty)('此字段必填');  
const validateEmail = validate(isEmail)('请输入有效邮箱');  
const validatePassword = validate(minLength(8))('密码至少8位');  

console.log(validateRequired(''));        // { valid: false, error: '此字段必填' }  
console.log(validateRequired('hello'));   // { valid: true, value: 'hello' }  
console.log(validateEmail('test@a.com')); // { valid: true, value: 'test@a.com' }  
console.log(validatePassword('123'));     // { valid: false, error: '密码至少8位' }

7.3 偏应用 (Partial Application)

偏应用是固定函数的一部分参数,返回一个接受剩余参数的新函数。

// 简单的偏应用函数  
function partial(fn, ...presetArgs) {  
    return function(...laterArgs) {  
        return fn(...presetArgs, ...laterArgs);  
    };  
}  

function greet(greeting, punctuation, name) {  
    return `${greeting}, ${name}${punctuation}`;  
}  

const greetHello = partial(greet, 'Hello', '!');  
console.log(greetHello('Alice'));   // "Hello, Alice!"  
console.log(greetHello('Bob'));     // "Hello, Bob!"  

const greetHi = partial(greet, 'Hi');  
console.log(greetHi('?', 'Charlie')); // "Hi, Charlie?"  

// 使用占位符的偏应用  
const _ = Symbol('placeholder');  

function partialWithPlaceholder(fn, ...presetArgs) {  
    return function(...laterArgs) {  
        let laterIndex = 0;  
        const args = presetArgs.map(arg =>   
            arg === _ ? laterArgs[laterIndex++] : arg  
        );  
        return fn(...args, ...laterArgs.slice(laterIndex));  
    };  
}  

function subtract(a, b) {  
    return a - b;  
}  

const subtractFrom10 = partialWithPlaceholder(subtract, 10, _);  
console.log(subtractFrom10(3)); // 7  

const subtract5 = partialWithPlaceholder(subtract, _, 5);  
console.log(subtract5(10)); // 5  

// 使用 bind 实现偏应用  
function multiply(a, b, c) {  
    return a * b * c;  
}  

const multiplyBy2 = multiply.bind(null, 2);  
console.log(multiplyBy2(3, 4)); // 24  

const multiplyBy2And3 = multiply.bind(null, 2, 3);  
console.log(multiplyBy2And3(4)); // 24

7.4 柯里化 vs 偏应用

// 柯里化:将 f(a, b, c) 转换为 f(a)(b)(c)  
// 每次只接受一个参数  

// 偏应用:固定部分参数,返回接受剩余参数的函数  
// 可以一次固定多个参数  

function example(a, b, c, d) {  
    return a + b + c + d;  
}  

// 柯里化后  
const curriedExample = curry(example);  
console.log(curriedExample(1)(2)(3)(4));  // 10  
console.log(curriedExample(1, 2)(3)(4));  // 10 (这是增强版柯里化)  

// 偏应用后  
const partialExample = partial(example, 1, 2);  
console.log(partialExample(3, 4)); // 10  

// 柯里化的一步步调用  
const step1 = curriedExample(1);  
const step2 = step1(2);  
const step3 = step2(3);  
const result = step3(4);  
console.log(result); // 10

8. 实战案例

8.1 数据处理管道

// 电商订单处理系统  
const orders = [  
    { id: 1, customer: 'Alice', items: [  
        { name: 'iPhone', price: 999, quantity: 1 },  
        { name: 'Case', price: 29, quantity: 2 }  
    ], date: '2024-01-15', status: 'completed' },  
    { id: 2, customer: 'Bob', items: [  
        { name: 'MacBook', price: 1299, quantity: 1 }  
    ], date: '2024-01-16', status: 'pending' },  
    { id: 3, customer: 'Charlie', items: [  
        { name: 'AirPods', price: 199, quantity: 2 },  
        { name: 'Charger', price: 29, quantity: 1 }  
    ], date: '2024-01-15', status: 'completed' },  
    { id: 4, customer: 'Alice', items: [  
        { name: 'iPad', price: 799, quantity: 1 }  
    ], date: '2024-01-17', status: 'completed' }  
];  

// 工具函数  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  
const curry = fn => function curried(...args) {  
    return args.length >= fn.length   
        ? fn(...args)   
        : (...more) => curried(...args, ...more);  
};  

// 基础操作函数(柯里化)  
const filter = curry((predicate, arr) => arr.filter(predicate));  
const map = curry((fn, arr) => arr.map(fn));  
const reduce = curry((reducer, initial, arr) => arr.reduce(reducer, initial));  
const sortBy = curry((fn, arr) => [...arr].sort((a, b) => fn(a) - fn(b)));  
const groupBy = curry((keyFn, arr) =>   
    arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        acc[key] = acc[key] || [];  
        acc[key].push(item);  
        return acc;  
    }, {})  
);  

// 业务逻辑函数  
const calculateOrderTotal = order => ({  
    ...order,  
    total: order.items.reduce((sum, item) => sum + item.price * item.quantity, 0)  
});  

const isCompleted = order => order.status === 'completed';  
const isCustomer = curry((name, order) => order.customer === name);  

// 1. 计算所有完成订单的总收入  
const totalRevenue = pipe(  
    filter(isCompleted),  
    map(calculateOrderTotal),  
    reduce((sum, order) => sum + order.total, 0)  
)(orders);  

console.log('总收入:', totalRevenue); // 2853  

// 2. 获取某个客户的订单统计  
const getCustomerStats = customerName => pipe(  
    filter(isCustomer(customerName)),  
    map(calculateOrderTotal),  
    orders => ({  
        customer: customerName,  
        orderCount: orders.length,  
        totalSpent: orders.reduce((sum, o) => sum + o.total, 0),  
        averageOrder: orders.length > 0   
            ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length   
            : 0  
    })  
)(orders);  

console.log('Alice的统计:', getCustomerStats('Alice'));  
// { customer: 'Alice', orderCount: 2, totalSpent: 1856, averageOrder: 928 }  

// 3. 按日期分组的订单报告  
const ordersByDate = pipe(  
    map(calculateOrderTotal),  
    groupBy(order => order.date),  
    Object.entries,  
    map(([date, orders]) => ({  
        date,  
        orderCount: orders.length,  
        totalRevenue: orders.reduce((sum, o) => sum + o.total, 0)  
    })),  
    sortBy(report => new Date(report.date))  
)(orders);  

console.log('按日期分组:');  
ordersByDate.forEach(report => {  
    console.log(`  ${report.date}: ${report.orderCount}单, $${report.totalRevenue}`);  
});  

// 4. 热销商品排行  
const topProducts = pipe(  
    flatMap => orders.flatMap(o => o.items),  
    reduce((acc, item) => {  
        acc[item.name] = (acc[item.name] || 0) + item.quantity;  
        return acc;  
    }, {}),  
    Object.entries,  
    map(([name, quantity]) => ({ name, quantity })),  
    arr => arr.sort((a, b) => b.quantity - a.quantity)  
)(orders);  

console.log('热销商品:', topProducts);

8.2 表单验证系统

// 函数式表单验证  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  
const curry = fn => (...args) =>   
    args.length >= fn.length ? fn(...args) : curry(fn.bind(null, ...args));  

// 验证结果类型  
const Success = value => ({  
    isSuccess: true,  
    value,  
    map: fn => Success(fn(value)),  
    flatMap: fn => fn(value),  
    getOrElse: () => value,  
    fold: (onError, onSuccess) => onSuccess(value)  
});  

const Failure = errors => ({  
    isSuccess: false,  
    errors,  
    map: () => Failure(errors),  
    flatMap: () => Failure(errors),  
    getOrElse: defaultValue => defaultValue,  
    fold: (onError, onSuccess) => onError(errors)  
});  

// 基础验证器  
const createValidator = curry((predicate, errorMessage, value) =>   
    predicate(value) ? Success(value) : Failure([errorMessage])  
);  

// 组合验证器  
const combineValidators = (...validators) => value => {  
    const results = validators.map(v => v(value));  
    const errors = results  
        .filter(r => !r.isSuccess)  
        .flatMap(r => r.errors);  

    return errors.length === 0 ? Success(value) : Failure(errors);  
};  

// 具体验证器  
const isRequired = createValidator(  
    v => v !== null && v !== undefined && v !== '',  
    '此字段必填'  
);  

const isEmail = createValidator(  
    v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),  
    '请输入有效的邮箱地址'  
);  

const minLength = min => createValidator(  
    v => v && v.length >= min,  
    `长度至少为 ${min} 个字符`  
);  

const maxLength = max => createValidator(  
    v => !v || v.length <= max,  
    `长度不能超过 ${max} 个字符`  
);  

const isNumber = createValidator(  
    v => !isNaN(Number(v)),  
    '必须是数字'  
);  

const inRange = (min, max) => createValidator(  
    v => {  
        const num = Number(v);  
        return num >= min && num <= max;  
    },  
    `必须在 ${min} 到 ${max} 之间`  
);  

const matches = regex => createValidator(  
    v => regex.test(v),  
    '格式不正确'  
);  

// 验证整个表单  
const validateField = (fieldName, value, ...validators) => {  
    const result = combineValidators(...validators)(value);  
    return result.fold(  
        errors => ({ [fieldName]: { valid: false, errors } }),  
        value => ({ [fieldName]: { valid: true, value } })  
    );  
};  

const validateForm = schema => formData => {  
    const results = Object.entries(schema).map(([field, validators]) =>   
        validateField(field, formData[field], ...validators)  
    );  

    const merged = results.reduce((acc, r) => ({ ...acc, ...r }), {});  
    const isValid = Object.values(merged).every(r => r.valid);  

    return { isValid, fields: merged };  
};  

// 使用示例  
const userFormSchema = {  
    username: [isRequired, minLength(3), maxLength(20)],  
    email: [isRequired, isEmail],  
    age: [isRequired, isNumber, inRange(18, 120)],  
    password: [isRequired, minLength(8), matches(/[A-Z]/), matches(/[0-9]/)]  
};  

const validateUserForm = validateForm(userFormSchema);  

// 测试有效数据  
const validData = {  
    username: 'johndoe',  
    email: 'john@example.com',  
    age: '25',  
    password: 'SecurePass123'  
};  

console.log('有效数据验证:');  
console.log(validateUserForm(validData));  
// { isValid: true, fields: { username: { valid: true, value: 'johndoe' }, ... } }  

// 测试无效数据  
const invalidData = {  
    username: 'ab',  
    email: 'invalid-email',  
    age: '15',  
    password: 'weak'  
};  

console.log('\n无效数据验证:');  
const result = validateUserForm(invalidData);  
console.log('isValid:', result.isValid);  
Object.entries(result.fields).forEach(([field, data]) => {  
    if (!data.valid) {  
        console.log(`  ${field}: ${data.errors.join(', ')}`);  
    }  
});

8.3 状态管理

// 简易的函数式状态管理  
const createStore = (reducer, initialState) => {  
    let state = initialState;  
    const listeners = [];  

    return {  
        getState: () => state,  
        dispatch: action => {  
            state = reducer(state, action);  
            listeners.forEach(listener => listener(state));  
            return action;  
        },  
        subscribe: listener => {  
            listeners.push(listener);  
            return () => {  
                const index = listeners.indexOf(listener);  
                if (index > -1) {  
                    listeners.splice(index, 1);  
                }  
            };  
        }  
    };  
};  

// Action creators  
const createAction = type => payload => ({ type, payload });  

const actions = {  
    addTodo: createAction('ADD_TODO'),  
    toggleTodo: createAction('TOGGLE_TODO'),  
    removeTodo: createAction('REMOVE_TODO'),  
    setFilter: createAction('SET_FILTER')  
};  

// Reducer(纯函数)  
const initialState = {  
    todos: [],  
    filter: 'all', // 'all', 'active', 'completed'  
    nextId: 1  
};  

const todoReducer = (state = initialState, action) => {  
    switch (action.type) {  
        case 'ADD_TODO':  
            return {  
                ...state,  
                todos: [  
                    ...state.todos,  
                    { id: state.nextId, text: action.payload, completed: false }  
                ],  
                nextId: state.nextId + 1  
            };  

        case 'TOGGLE_TODO':  
            return {  
                ...state,  
                todos: state.todos.map(todo =>  
                    todo.id === action.payload  
                        ? { ...todo, completed: !todo.completed }  
                        : todo  
                )  
            };  

        case 'REMOVE_TODO':  
            return {  
                ...state,  
                todos: state.todos.filter(todo => todo.id !== action.payload)  
            };  

        case 'SET_FILTER':  
            return {  
                ...state,  
                filter: action.payload  
            };  

        default:  
            return state;  
    }  
};  

// Selectors(派生状态)  
const selectTodos = state => state.todos;  
const selectFilter = state => state.filter;  

const selectFilteredTodos = state => {  
    const todos = selectTodos(state);  
    const filter = selectFilter(state);  

    switch (filter) {  
        case 'active':  
            return todos.filter(t => !t.completed);  
        case 'completed':  
            return todos.filter(t => t.completed);  
        default:  
            return todos;  
    }  
};  

const selectStats = state => {  
    const todos = selectTodos(state);  
    return {  
        total: todos.length,  
        completed: todos.filter(t => t.completed).length,  
        active: todos.filter(t => !t.completed).length  
    };  
};  

// 使用  
const store = createStore(todoReducer, initialState);  

// 订阅状态变化  
const unsubscribe = store.subscribe(state => {  
    console.log('\n当前状态:');  
    console.log('Todos:', selectFilteredTodos(state));  
    console.log('统计:', selectStats(state));  
});  

// 派发 actions  
console.log('=== 添加待办事项 ===');  
store.dispatch(actions.addTodo('学习函数式编程'));  
store.dispatch(actions.addTodo('写代码'));  
store.dispatch(actions.addTodo('看文档'));  

console.log('\n=== 完成一项 ===');  
store.dispatch(actions.toggleTodo(1));  

console.log('\n=== 设置过滤器为 active ===');  
store.dispatch(actions.setFilter('active'));  

console.log('\n=== 删除一项 ===');  
store.dispatch(actions.removeTodo(2));  

// 取消订阅  
unsubscribe();

8.4 函数式工具库

// 创建一个小型函数式工具库  
const FP = {  
    // 核心函数  
    pipe: (...fns) => x => fns.reduce((acc, fn) => fn(acc), x),  
    compose: (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x),  

    curry: fn => {  
        const curried = (...args) =>  
            args.length >= fn.length  
                ? fn(...args)  
                : (...more) => curried(...args, ...more);  
        return curried;  
    },  

    // 数组操作  
    map: fn => arr => arr.map(fn),  
    filter: predicate => arr => arr.filter(predicate),  
    reduce: (fn, initial) => arr => arr.reduce(fn, initial),  
    find: predicate => arr => arr.find(predicate),  
    some: predicate => arr => arr.some(predicate),  
    every: predicate => arr => arr.every(predicate),  

    // 对象操作  
    prop: key => obj => obj[key],  
    assoc: (key, value) => obj => ({ ...obj, [key]: value }),  
    omit: keys => obj => {  
        const result = { ...obj };  
        keys.forEach(key => delete result[key]);  
        return result;  
    },  
    pick: keys => obj =>   
        keys.reduce((acc, key) => {  
            if (key in obj) acc[key] = obj[key];  
            return acc;  
        }, {}),  

    // 逻辑操作  
    not: fn => (...args) => !fn(...args),  
    and: (f, g) => (...args) => f(...args) && g(...args),  
    or: (f, g) => (...args) => f(...args) || g(...args),  

    // 条件操作  
    when: (predicate, fn) => x => predicate(x) ? fn(x) : x,  
    unless: (predicate, fn) => x => predicate(x) ? x : fn(x),  
    ifElse: (predicate, onTrue, onFalse) => x =>   
        predicate(x) ? onTrue(x) : onFalse(x),  

    // 比较操作  
    equals: a => b => a === b,  
    gt: a => b => b > a,  
    gte: a => b => b >= a,  
    lt: a => b => b < a,  
    lte: a => b => b <= a,  

    // 数学操作  
    add: a => b => a + b,  
    subtract: a => b => b - a,  
    multiply: a => b => a * b,  
    divide: a => b => b / a,  

    // 字符串操作  
    split: separator => str => str.split(separator),  
    join: separator => arr => arr.join(separator),  
    trim: str => str.trim(),  
    toLowerCase: str => str.toLowerCase(),  
    toUpperCase: str => str.toUpperCase(),  

    // 实用工具  
    identity: x => x,  
    constant: x => () => x,  
    tap: fn => x => { fn(x); return x; },  

    // 数组工具  
    head: arr => arr[0],  
    tail: arr => arr.slice(1),  
    last: arr => arr[arr.length - 1],  
    init: arr => arr.slice(0, -1),  
    take: n => arr => arr.slice(0, n),  
    drop: n => arr => arr.slice(n),  

    // 分组和排序  
    groupBy: keyFn => arr => arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        acc[key] = acc[key] || [];  
        acc[key].push(item);  
        return acc;  
    }, {}),  

    sortBy: fn => arr => [...arr].sort((a, b) => {  
        const va = fn(a), vb = fn(b);  
        return va < vb ? -1 : va > vb ? 1 : 0;  
    }),  

    // 去重  
    uniq: arr => [...new Set(arr)],  
    uniqBy: fn => arr => {  
        const seen = new Set();  
        return arr.filter(item => {  
            const key = fn(item);  
            if (seen.has(key)) return false;  
            seen.add(key);  
            return true;  
        });  
    }  
};  

// 使用示例  
const { pipe, map, filter, reduce, prop, sortBy, take, groupBy } = FP;  

const users = [  
    { name: 'Alice', age: 25, department: 'Engineering' },  
    { name: 'Bob', age: 30, department: 'Marketing' },  
    { name: 'Charlie', age: 35, department: 'Engineering' },  
    { name: 'David', age: 28, department: 'Sales' },  
    { name: 'Eve', age: 32, department: 'Engineering' }  
];  

// 获取工程部门年龄最大的两个人的名字  
const result = pipe(  
    filter(user => user.department === 'Engineering'),  
    sortBy(prop('age')),  
    arr => arr.reverse(),  
    take(2),  
    map(prop('name'))  
)(users);  

console.log(result); // ['Charlie', 'Eve']  

// 按部门统计平均年龄  
const avgAgeByDept = pipe(  
    groupBy(prop('department')),  
    Object.entries,  
    map(([dept, members]) => ({  
        department: dept,  
        avgAge: members.reduce((sum, m) => sum + m.age, 0) / members.length,  
        count: members.length  
    })),  
    sortBy(prop('avgAge'))  
)(users);  

console.log(avgAgeByDept);

📖 总结

函数式编程的核心原则

  1. 使用纯函数 - 相同输入总是产生相同输出,无副作用
  2. 保持数据不可变 - 创建新数据而不是修改现有数据
  3. 使用高阶函数 - 函数可以作为参数传递和返回
  4. 函数组合 - 将简单函数组合成复杂函数
  5. 声明式编程 - 描述"做什么"而不是"怎么做"

学习路径建议

1. 基础阶段  
   ├── 理解纯函数概念  
   ├── 掌握 map、filter、reduce  
   └── 理解不可变性  

2. 进阶阶段  
   ├── 学习高阶函数模式  
   ├── 掌握函数组合和管道  
   └── 理解柯里化和偏应用  

3. 实践阶段  
   ├── 在项目中应用 FP 原则  
   ├── 使用 FP 库(Ramda、Lodash/fp)  
   └── 构建自己的工具函数库

推荐资源

记住:函数式编程不是全有或全无的选择。你可以在现有代码中逐步引入函数式概念,慢慢体会它带来的好处!

继续阅读 »

JavaScript 函数式编程完全指南

📚 目录

  1. 什么是函数式编程
  2. 纯函数
  3. 不可变性
  4. 高阶函数
  5. 核心数组方法
  6. 函数组合与管道
  7. 柯里化与偏应用
  8. 实战案例

1. 什么是函数式编程

函数式编程(Functional Programming,FP)是一种编程范式,它将计算视为数学函数的求值,强调:

  • 纯函数:相同输入总是产生相同输出
  • 不可变性:数据一旦创建就不能被修改
  • 函数组合:通过组合简单函数构建复杂功能
  • 避免副作用:函数不应该改变外部状态
// ❌ 命令式编程(告诉计算机"怎么做")  
const numbers = [1, 2, 3, 4, 5];  
const doubled = [];  
for (let i = 0; i < numbers.length; i++) {  
    doubled.push(numbers[i] * 2);  
}  
console.log(doubled); // [2, 4, 6, 8, 10]  

// ✅ 函数式编程(告诉计算机"做什么")  
const numbersFP = [1, 2, 3, 4, 5];  
const doubledFP = numbersFP.map(n => n * 2);  
console.log(doubledFP); // [2, 4, 6, 8, 10]

2. 纯函数

2.1 纯函数的定义

纯函数满足两个条件:

  1. 确定性:相同的输入永远返回相同的输出
  2. 无副作用:不修改外部状态,不依赖外部可变状态
// ✅ 纯函数示例  
function add(a, b) {  
    return a + b;  
}  

function multiply(a, b) {  
    return a * b;  
}  

function greet(name) {  
    return `Hello, ${name}!`;  
}  

// 多次调用,结果永远相同  
console.log(add(2, 3));      // 5  
console.log(add(2, 3));      // 5  
console.log(multiply(4, 5)); // 20  
console.log(greet('Alice')); // "Hello, Alice!"

2.2 非纯函数示例

// ❌ 非纯函数:依赖外部变量  
let taxRate = 0.1;  
function calculateTax(amount) {  
    return amount * taxRate; // 依赖外部变量 taxRate  
}  

console.log(calculateTax(100)); // 10  
taxRate = 0.2;  
console.log(calculateTax(100)); // 20 - 相同输入,不同输出!  

// ❌ 非纯函数:修改外部状态  
let total = 0;  
function addToTotal(value) {  
    total += value; // 副作用:修改了外部变量  
    return total;  
}  

console.log(addToTotal(5));  // 5  
console.log(addToTotal(5));  // 10 - 相同输入,不同输出!  

// ❌ 非纯函数:修改输入参数  
function addItem(cart, item) {  
    cart.push(item); // 副作用:修改了传入的数组  
    return cart;  
}  

const myCart = ['apple'];  
addItem(myCart, 'banana');  
console.log(myCart); // ['apple', 'banana'] - 原数组被修改了!

2.3 将非纯函数转换为纯函数

// ✅ 纯函数版本:将依赖作为参数传入  
function calculateTaxPure(amount, taxRate) {  
    return amount * taxRate;  
}  

console.log(calculateTaxPure(100, 0.1)); // 10  
console.log(calculateTaxPure(100, 0.2)); // 20  

// ✅ 纯函数版本:返回新值而不是修改外部状态  
function addToTotalPure(currentTotal, value) {  
    return currentTotal + value;  
}  

let totalPure = 0;  
totalPure = addToTotalPure(totalPure, 5);  
console.log(totalPure); // 5  

// ✅ 纯函数版本:返回新数组而不是修改原数组  
function addItemPure(cart, item) {  
    return [...cart, item]; // 返回新数组  
}  

const myCartPure = ['apple'];  
const newCart = addItemPure(myCartPure, 'banana');  
console.log(myCartPure); // ['apple'] - 原数组未被修改  
console.log(newCart);    // ['apple', 'banana']

2.4 纯函数的好处

// 1. 可测试性 - 纯函数非常容易测试  
function calculateDiscount(price, discountPercent) {  
    return price * (1 - discountPercent / 100);  
}  

// 测试用例  
console.log(calculateDiscount(100, 20) === 80);  // true  
console.log(calculateDiscount(50, 10) === 45);   // true  
console.log(calculateDiscount(200, 50) === 100); // true  

// 2. 可缓存性 - 相同输入总是相同输出,可以缓存结果  
function memoize(fn) {  
    const cache = new Map();  
    return function(...args) {  
        const key = JSON.stringify(args);  
        if (cache.has(key)) {  
            console.log('从缓存获取');  
            return cache.get(key);  
        }  
        console.log('计算中...');  
        const result = fn(...args);  
        cache.set(key, result);  
        return result;  
    };  
}  

function expensiveCalculation(n) {  
    // 模拟耗时计算  
    let result = 0;  
    for (let i = 0; i < n; i++) {  
        result += i;  
    }  
    return result;  
}  

const memoizedCalc = memoize(expensiveCalculation);  
console.log(memoizedCalc(10000)); // 计算中... 49995000  
console.log(memoizedCalc(10000)); // 从缓存获取 49995000  
console.log(memoizedCalc(5000));  // 计算中... 12497500

3. 不可变性

3.1 什么是不可变性

不可变性意味着数据一旦创建,就不能被修改。任何"修改"操作都会返回新的数据。

// ❌ 可变操作  
const person = { name: 'Alice', age: 25 };  
person.age = 26; // 直接修改原对象  
console.log(person); // { name: 'Alice', age: 26 }  

// ✅ 不可变操作  
const personImmutable = { name: 'Alice', age: 25 };  
const updatedPerson = { ...personImmutable, age: 26 }; // 创建新对象  
console.log(personImmutable); // { name: 'Alice', age: 25 } - 原对象不变  
console.log(updatedPerson);   // { name: 'Alice', age: 26 }

3.2 数组的不可变操作

const fruits = ['apple', 'banana', 'orange'];  

// ❌ 可变方法(会修改原数组)  
// push, pop, shift, unshift, splice, sort, reverse  

// ✅ 不可变方法(返回新数组)  
// map, filter, reduce, concat, slice, spread operator  

// 添加元素  
const withGrape = [...fruits, 'grape'];  
console.log(fruits);    // ['apple', 'banana', 'orange']  
console.log(withGrape); // ['apple', 'banana', 'orange', 'grape']  

// 在开头添加  
const withMango = ['mango', ...fruits];  
console.log(withMango); // ['mango', 'apple', 'banana', 'orange']  

// 删除元素(通过 filter)  
const withoutBanana = fruits.filter(f => f !== 'banana');  
console.log(withoutBanana); // ['apple', 'orange']  

// 修改元素(通过 map)  
const upperFruits = fruits.map(f => f.toUpperCase());  
console.log(upperFruits); // ['APPLE', 'BANANA', 'ORANGE']  

// 在指定位置插入  
const insertAt = (arr, index, item) => [  
    ...arr.slice(0, index),  
    item,  
    ...arr.slice(index)  
];  
console.log(insertAt(fruits, 1, 'kiwi')); // ['apple', 'kiwi', 'banana', 'orange']  

// 删除指定位置的元素  
const removeAt = (arr, index) => [  
    ...arr.slice(0, index),  
    ...arr.slice(index + 1)  
];  
console.log(removeAt(fruits, 1)); // ['apple', 'orange']  

// 更新指定位置的元素  
const updateAt = (arr, index, newValue) =>   
    arr.map((item, i) => i === index ? newValue : item);  
console.log(updateAt(fruits, 1, 'blueberry')); // ['apple', 'blueberry', 'orange']

3.3 对象的不可变操作

const user = {  
    name: 'Alice',  
    age: 25,  
    address: {  
        city: 'Beijing',  
        country: 'China'  
    },  
    hobbies: ['reading', 'coding']  
};  

// 更新顶层属性  
const userWithNewAge = { ...user, age: 26 };  
console.log(user.age);            // 25  
console.log(userWithNewAge.age);  // 26  

// 添加新属性  
const userWithEmail = { ...user, email: 'alice@example.com' };  
console.log(userWithEmail);  

// 删除属性  
const { age, ...userWithoutAge } = user;  
console.log(userWithoutAge); // { name: 'Alice', address: {...}, hobbies: [...] }  

// 更新嵌套属性(需要深层展开)  
const userWithNewCity = {  
    ...user,  
    address: {  
        ...user.address,  
        city: 'Shanghai'  
    }  
};  
console.log(user.address.city);           // 'Beijing'  
console.log(userWithNewCity.address.city); // 'Shanghai'  

// 更新数组属性  
const userWithNewHobby = {  
    ...user,  
    hobbies: [...user.hobbies, 'gaming']  
};  
console.log(user.hobbies);           // ['reading', 'coding']  
console.log(userWithNewHobby.hobbies); // ['reading', 'coding', 'gaming']

3.4 深度不可变更新工具函数

// 通用的深度更新函数  
function updatePath(obj, path, value) {  
    const keys = path.split('.');  

    if (keys.length === 1) {  
        return { ...obj, [keys[0]]: value };  
    }  

    const [first, ...rest] = keys;  
    return {  
        ...obj,  
        [first]: updatePath(obj[first], rest.join('.'), value)  
    };  
}  

const state = {  
    user: {  
        profile: {  
            name: 'Alice',  
            settings: {  
                theme: 'dark',  
                language: 'en'  
            }  
        }  
    }  
};  

const newState = updatePath(state, 'user.profile.settings.theme', 'light');  
console.log(state.user.profile.settings.theme);    // 'dark'  
console.log(newState.user.profile.settings.theme); // 'light'  

// 使用 Object.freeze 强制不可变(浅层)  
const frozenObj = Object.freeze({ a: 1, b: 2 });  
// frozenObj.a = 100; // 严格模式下会报错,非严格模式静默失败  
console.log(frozenObj.a); // 1  

// 深度冻结  
function deepFreeze(obj) {  
    Object.keys(obj).forEach(key => {  
        if (typeof obj[key] === 'object' && obj[key] !== null) {  
            deepFreeze(obj[key]);  
        }  
    });  
    return Object.freeze(obj);  
}  

const deepFrozenObj = deepFreeze({  
    a: 1,  
    b: { c: 2, d: { e: 3 } }  
});

4. 高阶函数

4.1 什么是高阶函数

高阶函数是至少满足以下条件之一的函数:

  1. 接受函数作为参数
  2. 返回一个函数
// 接受函数作为参数  
function executeOperation(a, b, operation) {  
    return operation(a, b);  
}  

const add = (x, y) => x + y;  
const multiply = (x, y) => x * y;  
const subtract = (x, y) => x - y;  

console.log(executeOperation(5, 3, add));      // 8  
console.log(executeOperation(5, 3, multiply)); // 15  
console.log(executeOperation(5, 3, subtract)); // 2  

// 返回一个函数  
function createMultiplier(factor) {  
    return function(number) {  
        return number * factor;  
    };  
}  

const double = createMultiplier(2);  
const triple = createMultiplier(3);  
const tenTimes = createMultiplier(10);  

console.log(double(5));   // 10  
console.log(triple(5));   // 15  
console.log(tenTimes(5)); // 50

4.2 常用高阶函数模式

// 1. 函数包装器 - 添加额外功能  
function withLogging(fn) {  
    return function(...args) {  
        console.log(`调用函数,参数: ${JSON.stringify(args)}`);  
        const result = fn(...args);  
        console.log(`返回结果: ${result}`);  
        return result;  
    };  
}  

function addNumbers(a, b) {  
    return a + b;  
}  

const addWithLogging = withLogging(addNumbers);  
addWithLogging(3, 4);  
// 输出:  
// 调用函数,参数: [3,4]  
// 返回结果: 7  

// 2. 函数计时器  
function withTiming(fn) {  
    return function(...args) {  
        const start = performance.now();  
        const result = fn(...args);  
        const end = performance.now();  
        console.log(`执行时间: ${(end - start).toFixed(2)}ms`);  
        return result;  
    };  
}  

function slowFunction() {  
    let sum = 0;  
    for (let i = 0; i < 1000000; i++) {  
        sum += i;  
    }  
    return sum;  
}  

const timedSlowFunction = withTiming(slowFunction);  
timedSlowFunction(); // 执行时间: x.xxms  

// 3. 只执行一次的函数  
function once(fn) {  
    let called = false;  
    let result;  

    return function(...args) {  
        if (!called) {  
            called = true;  
            result = fn(...args);  
        }  
        return result;  
    };  
}  

const initialize = once(() => {  
    console.log('初始化中...');  
    return { initialized: true };  
});  

console.log(initialize()); // 初始化中... { initialized: true }  
console.log(initialize()); // { initialized: true } - 不再打印"初始化中..."  
console.log(initialize()); // { initialized: true }  

// 4. 防抖函数  
function debounce(fn, delay) {  
    let timeoutId;  

    return function(...args) {  
        clearTimeout(timeoutId);  
        timeoutId = setTimeout(() => {  
            fn.apply(this, args);  
        }, delay);  
    };  
}  

const debouncedSearch = debounce((query) => {  
    console.log(`搜索: ${query}`);  
}, 300);  

// 快速连续调用,只有最后一次会执行  
debouncedSearch('a');  
debouncedSearch('ab');  
debouncedSearch('abc');  
// 300ms 后只输出: 搜索: abc  

// 5. 节流函数  
function throttle(fn, limit) {  
    let inThrottle = false;  

    return function(...args) {  
        if (!inThrottle) {  
            fn.apply(this, args);  
            inThrottle = true;  
            setTimeout(() => {  
                inThrottle = false;  
            }, limit);  
        }  
    };  
}  

const throttledScroll = throttle(() => {  
    console.log('处理滚动事件');  
}, 100);

4.3 创建专用函数

// 使用高阶函数创建专用函数  
function createValidator(validationFn, errorMessage) {  
    return function(value) {  
        if (validationFn(value)) {  
            return { valid: true, value };  
        }  
        return { valid: false, error: errorMessage };  
    };  
}  

const isNotEmpty = createValidator(  
    value => value && value.trim().length > 0,  
    '值不能为空'  
);  

const isEmail = createValidator(  
    value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),  
    '请输入有效的邮箱地址'  
);  

const isMinLength = (min) => createValidator(  
    value => value && value.length >= min,  
    `长度至少为 ${min} 个字符`  
);  

console.log(isNotEmpty('hello'));     // { valid: true, value: 'hello' }  
console.log(isNotEmpty(''));          // { valid: false, error: '值不能为空' }  
console.log(isEmail('test@mail.com')); // { valid: true, value: 'test@mail.com' }  
console.log(isEmail('invalid'));       // { valid: false, error: '请输入有效的邮箱地址' }  

const isMinLength5 = isMinLength(5);  
console.log(isMinLength5('hello'));    // { valid: true, value: 'hello' }  
console.log(isMinLength5('hi'));       // { valid: false, error: '长度至少为 5 个字符' }

5. 核心数组方法

5.1 map - 转换每个元素

// map 的基本用法  
const numbers = [1, 2, 3, 4, 5];  

// 简单转换  
const doubled = numbers.map(n => n * 2);  
console.log(doubled); // [2, 4, 6, 8, 10]  

const squared = numbers.map(n => n ** 2);  
console.log(squared); // [1, 4, 9, 16, 25]  

// 转换对象数组  
const users = [  
    { name: 'Alice', age: 25 },  
    { name: 'Bob', age: 30 },  
    { name: 'Charlie', age: 35 }  
];  

const names = users.map(user => user.name);  
console.log(names); // ['Alice', 'Bob', 'Charlie']  

const userCards = users.map(user => ({  
    ...user,  
    displayName: `${user.name} (${user.age}岁)`  
}));  
console.log(userCards);  
// [  
//   { name: 'Alice', age: 25, displayName: 'Alice (25岁)' },  
//   { name: 'Bob', age: 30, displayName: 'Bob (30岁)' },  
//   { name: 'Charlie', age: 35, displayName: 'Charlie (35岁)' }  
// ]  

// 使用索引参数  
const indexed = numbers.map((n, index) => `${index}: ${n}`);  
console.log(indexed); // ['0: 1', '1: 2', '2: 3', '3: 4', '4: 5']  

// 自己实现 map  
function myMap(arr, fn) {  
    const result = [];  
    for (let i = 0; i < arr.length; i++) {  
        result.push(fn(arr[i], i, arr));  
    }  
    return result;  
}  

console.log(myMap([1, 2, 3], x => x * 10)); // [10, 20, 30]

5.2 filter - 过滤元素

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  

// 基本过滤  
const evens = numbers.filter(n => n % 2 === 0);  
console.log(evens); // [2, 4, 6, 8, 10]  

const odds = numbers.filter(n => n % 2 !== 0);  
console.log(odds); // [1, 3, 5, 7, 9]  

const greaterThan5 = numbers.filter(n => n > 5);  
console.log(greaterThan5); // [6, 7, 8, 9, 10]  

// 过滤对象数组  
const products = [  
    { name: 'iPhone', price: 999, inStock: true },  
    { name: 'iPad', price: 799, inStock: false },  
    { name: 'MacBook', price: 1299, inStock: true },  
    { name: 'AirPods', price: 199, inStock: true }  
];  

const inStockProducts = products.filter(p => p.inStock);  
console.log(inStockProducts);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'MacBook', price: 1299, inStock: true },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

const affordableProducts = products.filter(p => p.price < 1000);  
console.log(affordableProducts);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'iPad', price: 799, inStock: false },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

// 组合条件  
const affordableInStock = products.filter(p => p.price < 1000 && p.inStock);  
console.log(affordableInStock);  
// [  
//   { name: 'iPhone', price: 999, inStock: true },  
//   { name: 'AirPods', price: 199, inStock: true }  
// ]  

// 去除假值  
const mixedArray = [0, 1, '', 'hello', null, undefined, false, true, NaN];  
const truthyValues = mixedArray.filter(Boolean);  
console.log(truthyValues); // [1, 'hello', true]  

// 去重  
const duplicates = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];  
const unique = duplicates.filter((item, index, arr) => arr.indexOf(item) === index);  
console.log(unique); // [1, 2, 3, 4]  

// 自己实现 filter  
function myFilter(arr, predicate) {  
    const result = [];  
    for (let i = 0; i < arr.length; i++) {  
        if (predicate(arr[i], i, arr)) {  
            result.push(arr[i]);  
        }  
    }  
    return result;  
}  

console.log(myFilter([1, 2, 3, 4, 5], x => x > 2)); // [3, 4, 5]

5.3 reduce - 归约为单一值

// 基本用法:求和  
const numbers = [1, 2, 3, 4, 5];  

const sum = numbers.reduce((accumulator, current) => {  
    console.log(`accumulator: ${accumulator}, current: ${current}`);  
    return accumulator + current;  
}, 0);  
// accumulator: 0, current: 1  
// accumulator: 1, current: 2  
// accumulator: 3, current: 3  
// accumulator: 6, current: 4  
// accumulator: 10, current: 5  
console.log(sum); // 15  

// 求乘积  
const product = numbers.reduce((acc, cur) => acc * cur, 1);  
console.log(product); // 120  

// 找最大值  
const max = numbers.reduce((acc, cur) => cur > acc ? cur : acc, -Infinity);  
console.log(max); // 5  

// 找最小值  
const min = numbers.reduce((acc, cur) => cur < acc ? cur : acc, Infinity);  
console.log(min); // 1  

// 数组转对象  
const users = [  
    { id: 1, name: 'Alice' },  
    { id: 2, name: 'Bob' },  
    { id: 3, name: 'Charlie' }  
];  

const userMap = users.reduce((acc, user) => {  
    acc[user.id] = user;  
    return acc;  
}, {});  
console.log(userMap);  
// {  
//   1: { id: 1, name: 'Alice' },  
//   2: { id: 2, name: 'Bob' },  
//   3: { id: 3, name: 'Charlie' }  
// }  

// 分组  
const people = [  
    { name: 'Alice', age: 25, city: 'Beijing' },  
    { name: 'Bob', age: 30, city: 'Shanghai' },  
    { name: 'Charlie', age: 25, city: 'Beijing' },  
    { name: 'David', age: 30, city: 'Beijing' }  
];  

const groupByAge = people.reduce((acc, person) => {  
    const key = person.age;  
    if (!acc[key]) {  
        acc[key] = [];  
    }  
    acc[key].push(person);  
    return acc;  
}, {});  
console.log(groupByAge);  
// {  
//   25: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],  
//   30: [{ name: 'Bob', ... }, { name: 'David', ... }]  
// }  

// 通用分组函数  
function groupBy(arr, keyFn) {  
    return arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        if (!acc[key]) {  
            acc[key] = [];  
        }  
        acc[key].push(item);  
        return acc;  
    }, {});  
}  

console.log(groupBy(people, p => p.city));  
// {  
//   Beijing: [Alice, Charlie, David],  
//   Shanghai: [Bob]  
// }  

// 统计出现次数  
const words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];  

const wordCount = words.reduce((acc, word) => {  
    acc[word] = (acc[word] || 0) + 1;  
    return acc;  
}, {});  
console.log(wordCount); // { apple: 3, banana: 2, orange: 1 }  

// 扁平化数组  
const nested = [[1, 2], [3, 4], [5, 6]];  
const flattened = nested.reduce((acc, arr) => [...acc, ...arr], []);  
console.log(flattened); // [1, 2, 3, 4, 5, 6]  

// 深度扁平化  
const deepNested = [[1, [2, 3]], [4, [5, [6, 7]]]];  

function flatten(arr) {  
    return arr.reduce((acc, item) => {  
        if (Array.isArray(item)) {  
            return [...acc, ...flatten(item)];  
        }  
        return [...acc, item];  
    }, []);  
}  

console.log(flatten(deepNested)); // [1, 2, 3, 4, 5, 6, 7]  

// 使用 reduce 实现 map  
function mapWithReduce(arr, fn) {  
    return arr.reduce((acc, item, index) => {  
        acc.push(fn(item, index, arr));  
        return acc;  
    }, []);  
}  

console.log(mapWithReduce([1, 2, 3], x => x * 2)); // [2, 4, 6]  

// 使用 reduce 实现 filter  
function filterWithReduce(arr, predicate) {  
    return arr.reduce((acc, item, index) => {  
        if (predicate(item, index, arr)) {  
            acc.push(item);  
        }  
        return acc;  
    }, []);  
}  

console.log(filterWithReduce([1, 2, 3, 4, 5], x => x > 2)); // [3, 4, 5]  

// 自己实现 reduce  
function myReduce(arr, reducer, initialValue) {  
    let accumulator = initialValue;  
    let startIndex = 0;  

    if (arguments.length < 3) {  
        accumulator = arr[0];  
        startIndex = 1;  
    }  

    for (let i = startIndex; i < arr.length; i++) {  
        accumulator = reducer(accumulator, arr[i], i, arr);  
    }  

    return accumulator;  
}  

console.log(myReduce([1, 2, 3, 4, 5], (a, b) => a + b, 0)); // 15

5.4 其他有用的数组方法

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  

// find - 找到第一个满足条件的元素  
const firstEven = numbers.find(n => n % 2 === 0);  
console.log(firstEven); // 2  

const firstGreaterThan5 = numbers.find(n => n > 5);  
console.log(firstGreaterThan5); // 6  

// findIndex - 找到第一个满足条件的元素的索引  
const firstEvenIndex = numbers.findIndex(n => n % 2 === 0);  
console.log(firstEvenIndex); // 1  

// some - 检查是否至少有一个元素满足条件  
const hasEven = numbers.some(n => n % 2 === 0);  
console.log(hasEven); // true  

const hasNegative = numbers.some(n => n < 0);  
console.log(hasNegative); // false  

// every - 检查是否所有元素都满足条件  
const allPositive = numbers.every(n => n > 0);  
console.log(allPositive); // true  

const allEven = numbers.every(n => n % 2 === 0);  
console.log(allEven); // false  

// includes - 检查数组是否包含某个值  
console.log(numbers.includes(5));  // true  
console.log(numbers.includes(11)); // false  

// flat - 扁平化数组  
const nestedArray = [1, [2, 3], [4, [5, 6]]];  
console.log(nestedArray.flat());    // [1, 2, 3, 4, [5, 6]]  
console.log(nestedArray.flat(2));   // [1, 2, 3, 4, 5, 6]  
console.log(nestedArray.flat(Infinity)); // [1, 2, 3, 4, 5, 6]  

// flatMap - map + flat(1)  
const sentences = ['Hello World', 'Goodbye World'];  
const words = sentences.flatMap(s => s.split(' '));  
console.log(words); // ['Hello', 'World', 'Goodbye', 'World']  

// 实用示例:处理可能返回数组的映射  
const data = [1, 2, 3];  
const duplicated = data.flatMap(n => [n, n]);  
console.log(duplicated); // [1, 1, 2, 2, 3, 3]

5.5 方法链式调用

const orders = [  
    { id: 1, customer: 'Alice', items: ['apple', 'banana'], total: 25, status: 'completed' },  
    { id: 2, customer: 'Bob', items: ['orange'], total: 15, status: 'pending' },  
    { id: 3, customer: 'Charlie', items: ['apple', 'grape', 'melon'], total: 45, status: 'completed' },  
    { id: 4, customer: 'David', items: ['banana'], total: 10, status: 'cancelled' },  
    { id: 5, customer: 'Eve', items: ['apple', 'orange'], total: 30, status: 'completed' }  
];  

// 链式调用:筛选已完成订单,计算平均订单金额  
const averageCompletedOrderValue = orders  
    .filter(order => order.status === 'completed')  
    .map(order => order.total)  
    .reduce((sum, total, _, arr) => sum + total / arr.length, 0);  

console.log(averageCompletedOrderValue); // 33.33...  

// 获取所有完成订单的商品列表(去重)  
const completedOrderItems = orders  
    .filter(order => order.status === 'completed')  
    .flatMap(order => order.items)  
    .filter((item, index, arr) => arr.indexOf(item) === index);  

console.log(completedOrderItems); // ['apple', 'banana', 'grape', 'melon', 'orange']  

// 创建订单摘要  
const orderSummary = orders  
    .filter(order => order.status !== 'cancelled')  
    .map(order => ({  
        orderId: order.id,  
        customer: order.customer,  
        itemCount: order.items.length,  
        total: order.total  
    }))  
    .sort((a, b) => b.total - a.total);  

console.log(orderSummary);  
// [  
//   { orderId: 3, customer: 'Charlie', itemCount: 3, total: 45 },  
//   { orderId: 5, customer: 'Eve', itemCount: 2, total: 30 },  
//   { orderId: 1, customer: 'Alice', itemCount: 2, total: 25 },  
//   { orderId: 2, customer: 'Bob', itemCount: 1, total: 15 }  
// ]  

// 按状态分组统计  
const statusStats = orders.reduce((acc, order) => {  
    if (!acc[order.status]) {  
        acc[order.status] = { count: 0, totalValue: 0 };  
    }  
    acc[order.status].count++;  
    acc[order.status].totalValue += order.total;  
    return acc;  
}, {});  

console.log(statusStats);  
// {  
//   completed: { count: 3, totalValue: 100 },  
//   pending: { count: 1, totalValue: 15 },  
//   cancelled: { count: 1, totalValue: 10 }  
// }

6. 函数组合与管道

6.1 函数组合 (Compose)

函数组合是将多个函数合并成一个函数,从右到左执行。

// 基本概念  
// compose(f, g, h)(x) 等价于 f(g(h(x)))  

// 简单的两个函数组合  
const compose2 = (f, g) => x => f(g(x));  

const addOne = x => x + 1;  
const double = x => x * 2;  

const addOneThenDouble = compose2(double, addOne); // 先加1,再乘2  
console.log(addOneThenDouble(5)); // (5 + 1) * 2 = 12  

const doubleThenAddOne = compose2(addOne, double); // 先乘2,再加1  
console.log(doubleThenAddOne(5)); // (5 * 2) + 1 = 11  

// 通用的 compose 函数(支持多个函数)  
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);  

const square = x => x ** 2;  
const negate = x => -x;  

const composed = compose(negate, square, addOne, double);  
// 执行顺序: double -> addOne -> square -> negate  
// 5 -> 10 -> 11 -> 121 -> -121  
console.log(composed(5)); // -121  

// 更复杂的例子:处理字符串  
const trim = str => str.trim();  
const toLowerCase = str => str.toLowerCase();  
const split = delimiter => str => str.split(delimiter);  
const join = delimiter => arr => arr.join(delimiter);  
const map = fn => arr => arr.map(fn);  
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);  

const slugify = compose(  
    join('-'),  
    map(toLowerCase),  
    split(' '),  
    trim  
);  

console.log(slugify('  Hello World  ')); // 'hello-world'  
console.log(slugify('JavaScript Is Awesome')); // 'javascript-is-awesome'

6.2 管道 (Pipe)

管道与组合类似,但从左到右执行,更符合阅读习惯。

// pipe(f, g, h)(x) 等价于 h(g(f(x)))  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  

const addOne = x => x + 1;  
const double = x => x * 2;  
const square = x => x ** 2;  

const piped = pipe(double, addOne, square);  
// 执行顺序: double -> addOne -> square  
// 5 -> 10 -> 11 -> 121  
console.log(piped(5)); // 121  

// 实际应用示例:数据处理管道  
const users = [  
    { name: 'alice', age: 25, role: 'admin' },  
    { name: 'bob', age: 30, role: 'user' },  
    { name: 'charlie', age: 35, role: 'admin' },  
    { name: 'david', age: 28, role: 'user' }  
];  

// 辅助函数  
const filter = predicate => arr => arr.filter(predicate);  
const map = fn => arr => arr.map(fn);  
const sortBy = key => arr => [...arr].sort((a, b) => a[key] - b[key]);  
const take = n => arr => arr.slice(0, n);  

// 构建处理管道  
const getTopAdminNames = pipe(  
    filter(u => u.role === 'admin'),  // 筛选管理员  
    sortBy('age'),                      // 按年龄排序  
    map(u => u.name.toUpperCase()),    // 获取大写名字  
    take(2)                             // 取前两个  
);  

console.log(getTopAdminNames(users)); // ['ALICE', 'CHARLIE']

6.3 异步管道

// 支持异步函数的管道  
const pipeAsync = (...fns) => initialValue =>  
    fns.reduce(  
        (promise, fn) => promise.then(fn),  
        Promise.resolve(initialValue)  
    );  

// 模拟异步操作  
const fetchUser = async (id) => {  
    console.log(`获取用户 ${id}...`);  
    return { id, name: 'Alice', email: 'alice@example.com' };  
};  

const fetchUserPosts = async (user) => {  
    console.log(`获取 ${user.name} 的帖子...`);  
    return {  
        ...user,  
        posts: ['Post 1', 'Post 2', 'Post 3']  
    };  
};  

const formatUserData = async (data) => {  
    console.log('格式化数据...');  
    return {  
        displayName: data.name.toUpperCase(),  
        email: data.email,  
        postCount: data.posts.length  
    };  
};  

const processUser = pipeAsync(  
    fetchUser,  
    fetchUserPosts,  
    formatUserData  
);  

processUser(1).then(console.log);  
// 获取用户 1...  
// 获取 Alice 的帖子...  
// 格式化数据...  
// { displayName: 'ALICE', email: 'alice@example.com', postCount: 3 }

6.4 条件组合

// 创建条件执行函数  
const when = (predicate, fn) => x => predicate(x) ? fn(x) : x;  

const unless = (predicate, fn) => x => predicate(x) ? x : fn(x);  

const isEven = x => x % 2 === 0;  
const double = x => x * 2;  
const addOne = x => x + 1;  

const doubleIfEven = when(isEven, double);  
console.log(doubleIfEven(4)); // 8  
console.log(doubleIfEven(5)); // 5  

const addOneIfOdd = unless(isEven, addOne);  
console.log(addOneIfOdd(4)); // 4  
console.log(addOneIfOdd(5)); // 6  

// 分支组合  
const ifElse = (predicate, onTrue, onFalse) => x =>  
    predicate(x) ? onTrue(x) : onFalse(x);  

const processNumber = ifElse(  
    isEven,  
    x => `${x} 是偶数,乘2得 ${x * 2}`,  
    x => `${x} 是奇数,加1得 ${x + 1}`  
);  

console.log(processNumber(4)); // "4 是偶数,乘2得 8"  
console.log(processNumber(5)); // "5 是奇数,加1得 6"  

// 多条件分支  
const cond = (...pairs) => x => {  
    for (const [predicate, fn] of pairs) {  
        if (predicate(x)) {  
            return fn(x);  
        }  
    }  
    return x;  
};  

const classifyAge = cond(  
    [age => age < 13, () => '儿童'],  
    [age => age < 20, () => '青少年'],  
    [age => age < 60, () => '成年人'],  
    [() => true, () => '老年人']  
);  

console.log(classifyAge(8));  // "儿童"  
console.log(classifyAge(15)); // "青少年"  
console.log(classifyAge(30)); // "成年人"  
console.log(classifyAge(70)); // "老年人"

7. 柯里化与偏应用

7.1 柯里化 (Currying)

柯里化是将一个接受多个参数的函数转换为一系列接受单个参数的函数。

// 普通函数  
function add(a, b, c) {  
    return a + b + c;  
}  
console.log(add(1, 2, 3)); // 6  

// 手动柯里化  
function addCurried(a) {  
    return function(b) {  
        return function(c) {  
            return a + b + c;  
        };  
    };  
}  
console.log(addCurried(1)(2)(3)); // 6  

// 箭头函数版本  
const addCurriedArrow = a => b => c => a + b + c;  
console.log(addCurriedArrow(1)(2)(3)); // 6  

// 部分应用  
const add1 = addCurriedArrow(1);  
const add1and2 = add1(2);  
console.log(add1and2(3)); // 6  
console.log(add1and2(10)); // 13  

// 通用柯里化函数  
function curry(fn) {  
    return function curried(...args) {  
        if (args.length >= fn.length) {  
            return fn.apply(this, args);  
        }  
        return function(...moreArgs) {  
            return curried.apply(this, [...args, ...moreArgs]);  
        };  
    };  
}  

// 使用示例  
function multiply(a, b, c) {  
    return a * b * c;  
}  

const curriedMultiply = curry(multiply);  

console.log(curriedMultiply(2)(3)(4));     // 24  
console.log(curriedMultiply(2, 3)(4));     // 24  
console.log(curriedMultiply(2)(3, 4));     // 24  
console.log(curriedMultiply(2, 3, 4));     // 24  

// 创建专用函数  
const double = curriedMultiply(2)(1);  
console.log(double(5));  // 10  
console.log(double(10)); // 20

7.2 柯里化的实际应用

// 1. 配置化的API请求  
const request = curry((method, baseUrl, endpoint, data) => {  
    console.log(`${method} ${baseUrl}${endpoint}`, data);  
    // 实际实现会发送真实请求  
    return { method, url: `${baseUrl}${endpoint}`, data };  
});  

const apiRequest = request('POST')('https://api.example.com');  
const createUser = apiRequest('/users');  
const createPost = apiRequest('/posts');  

createUser({ name: 'Alice' }); // POST https://api.example.com/users { name: 'Alice' }  
createPost({ title: 'Hello' }); // POST https://api.example.com/posts { title: 'Hello' }  

// 2. 格式化函数  
const formatCurrency = curry((symbol, decimals, amount) => {  
    return `${symbol}${amount.toFixed(decimals)}`;  
});  

const formatUSD = formatCurrency('$')(2);  
const formatEUR = formatCurrency('€')(2);  
const formatJPY = formatCurrency('¥')(0);  

console.log(formatUSD(1234.5));   // "$1234.50"  
console.log(formatEUR(1234.5));   // "€1234.50"  
console.log(formatJPY(1234.5));   // "¥1235"  

// 3. 事件处理  
const handleEvent = curry((handler, eventName, element) => {  
    element.addEventListener(eventName, handler);  
    return () => element.removeEventListener(eventName, handler);  
});  

const logEvent = event => console.log('Event:', event.type);  
const addClickHandler = handleEvent(logEvent)('click');  

// addClickHandler(document.getElementById('myButton'));  

// 4. 验证函数  
const validate = curry((validator, errorMsg, value) => {  
    return validator(value)   
        ? { valid: true, value }  
        : { valid: false, error: errorMsg };  
});  

const isNotEmpty = value => value && value.trim().length > 0;  
const isEmail = value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);  
const minLength = min => value => value && value.length >= min;  

const validateRequired = validate(isNotEmpty)('此字段必填');  
const validateEmail = validate(isEmail)('请输入有效邮箱');  
const validatePassword = validate(minLength(8))('密码至少8位');  

console.log(validateRequired(''));        // { valid: false, error: '此字段必填' }  
console.log(validateRequired('hello'));   // { valid: true, value: 'hello' }  
console.log(validateEmail('test@a.com')); // { valid: true, value: 'test@a.com' }  
console.log(validatePassword('123'));     // { valid: false, error: '密码至少8位' }

7.3 偏应用 (Partial Application)

偏应用是固定函数的一部分参数,返回一个接受剩余参数的新函数。

// 简单的偏应用函数  
function partial(fn, ...presetArgs) {  
    return function(...laterArgs) {  
        return fn(...presetArgs, ...laterArgs);  
    };  
}  

function greet(greeting, punctuation, name) {  
    return `${greeting}, ${name}${punctuation}`;  
}  

const greetHello = partial(greet, 'Hello', '!');  
console.log(greetHello('Alice'));   // "Hello, Alice!"  
console.log(greetHello('Bob'));     // "Hello, Bob!"  

const greetHi = partial(greet, 'Hi');  
console.log(greetHi('?', 'Charlie')); // "Hi, Charlie?"  

// 使用占位符的偏应用  
const _ = Symbol('placeholder');  

function partialWithPlaceholder(fn, ...presetArgs) {  
    return function(...laterArgs) {  
        let laterIndex = 0;  
        const args = presetArgs.map(arg =>   
            arg === _ ? laterArgs[laterIndex++] : arg  
        );  
        return fn(...args, ...laterArgs.slice(laterIndex));  
    };  
}  

function subtract(a, b) {  
    return a - b;  
}  

const subtractFrom10 = partialWithPlaceholder(subtract, 10, _);  
console.log(subtractFrom10(3)); // 7  

const subtract5 = partialWithPlaceholder(subtract, _, 5);  
console.log(subtract5(10)); // 5  

// 使用 bind 实现偏应用  
function multiply(a, b, c) {  
    return a * b * c;  
}  

const multiplyBy2 = multiply.bind(null, 2);  
console.log(multiplyBy2(3, 4)); // 24  

const multiplyBy2And3 = multiply.bind(null, 2, 3);  
console.log(multiplyBy2And3(4)); // 24

7.4 柯里化 vs 偏应用

// 柯里化:将 f(a, b, c) 转换为 f(a)(b)(c)  
// 每次只接受一个参数  

// 偏应用:固定部分参数,返回接受剩余参数的函数  
// 可以一次固定多个参数  

function example(a, b, c, d) {  
    return a + b + c + d;  
}  

// 柯里化后  
const curriedExample = curry(example);  
console.log(curriedExample(1)(2)(3)(4));  // 10  
console.log(curriedExample(1, 2)(3)(4));  // 10 (这是增强版柯里化)  

// 偏应用后  
const partialExample = partial(example, 1, 2);  
console.log(partialExample(3, 4)); // 10  

// 柯里化的一步步调用  
const step1 = curriedExample(1);  
const step2 = step1(2);  
const step3 = step2(3);  
const result = step3(4);  
console.log(result); // 10

8. 实战案例

8.1 数据处理管道

// 电商订单处理系统  
const orders = [  
    { id: 1, customer: 'Alice', items: [  
        { name: 'iPhone', price: 999, quantity: 1 },  
        { name: 'Case', price: 29, quantity: 2 }  
    ], date: '2024-01-15', status: 'completed' },  
    { id: 2, customer: 'Bob', items: [  
        { name: 'MacBook', price: 1299, quantity: 1 }  
    ], date: '2024-01-16', status: 'pending' },  
    { id: 3, customer: 'Charlie', items: [  
        { name: 'AirPods', price: 199, quantity: 2 },  
        { name: 'Charger', price: 29, quantity: 1 }  
    ], date: '2024-01-15', status: 'completed' },  
    { id: 4, customer: 'Alice', items: [  
        { name: 'iPad', price: 799, quantity: 1 }  
    ], date: '2024-01-17', status: 'completed' }  
];  

// 工具函数  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  
const curry = fn => function curried(...args) {  
    return args.length >= fn.length   
        ? fn(...args)   
        : (...more) => curried(...args, ...more);  
};  

// 基础操作函数(柯里化)  
const filter = curry((predicate, arr) => arr.filter(predicate));  
const map = curry((fn, arr) => arr.map(fn));  
const reduce = curry((reducer, initial, arr) => arr.reduce(reducer, initial));  
const sortBy = curry((fn, arr) => [...arr].sort((a, b) => fn(a) - fn(b)));  
const groupBy = curry((keyFn, arr) =>   
    arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        acc[key] = acc[key] || [];  
        acc[key].push(item);  
        return acc;  
    }, {})  
);  

// 业务逻辑函数  
const calculateOrderTotal = order => ({  
    ...order,  
    total: order.items.reduce((sum, item) => sum + item.price * item.quantity, 0)  
});  

const isCompleted = order => order.status === 'completed';  
const isCustomer = curry((name, order) => order.customer === name);  

// 1. 计算所有完成订单的总收入  
const totalRevenue = pipe(  
    filter(isCompleted),  
    map(calculateOrderTotal),  
    reduce((sum, order) => sum + order.total, 0)  
)(orders);  

console.log('总收入:', totalRevenue); // 2853  

// 2. 获取某个客户的订单统计  
const getCustomerStats = customerName => pipe(  
    filter(isCustomer(customerName)),  
    map(calculateOrderTotal),  
    orders => ({  
        customer: customerName,  
        orderCount: orders.length,  
        totalSpent: orders.reduce((sum, o) => sum + o.total, 0),  
        averageOrder: orders.length > 0   
            ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length   
            : 0  
    })  
)(orders);  

console.log('Alice的统计:', getCustomerStats('Alice'));  
// { customer: 'Alice', orderCount: 2, totalSpent: 1856, averageOrder: 928 }  

// 3. 按日期分组的订单报告  
const ordersByDate = pipe(  
    map(calculateOrderTotal),  
    groupBy(order => order.date),  
    Object.entries,  
    map(([date, orders]) => ({  
        date,  
        orderCount: orders.length,  
        totalRevenue: orders.reduce((sum, o) => sum + o.total, 0)  
    })),  
    sortBy(report => new Date(report.date))  
)(orders);  

console.log('按日期分组:');  
ordersByDate.forEach(report => {  
    console.log(`  ${report.date}: ${report.orderCount}单, $${report.totalRevenue}`);  
});  

// 4. 热销商品排行  
const topProducts = pipe(  
    flatMap => orders.flatMap(o => o.items),  
    reduce((acc, item) => {  
        acc[item.name] = (acc[item.name] || 0) + item.quantity;  
        return acc;  
    }, {}),  
    Object.entries,  
    map(([name, quantity]) => ({ name, quantity })),  
    arr => arr.sort((a, b) => b.quantity - a.quantity)  
)(orders);  

console.log('热销商品:', topProducts);

8.2 表单验证系统

// 函数式表单验证  
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);  
const curry = fn => (...args) =>   
    args.length >= fn.length ? fn(...args) : curry(fn.bind(null, ...args));  

// 验证结果类型  
const Success = value => ({  
    isSuccess: true,  
    value,  
    map: fn => Success(fn(value)),  
    flatMap: fn => fn(value),  
    getOrElse: () => value,  
    fold: (onError, onSuccess) => onSuccess(value)  
});  

const Failure = errors => ({  
    isSuccess: false,  
    errors,  
    map: () => Failure(errors),  
    flatMap: () => Failure(errors),  
    getOrElse: defaultValue => defaultValue,  
    fold: (onError, onSuccess) => onError(errors)  
});  

// 基础验证器  
const createValidator = curry((predicate, errorMessage, value) =>   
    predicate(value) ? Success(value) : Failure([errorMessage])  
);  

// 组合验证器  
const combineValidators = (...validators) => value => {  
    const results = validators.map(v => v(value));  
    const errors = results  
        .filter(r => !r.isSuccess)  
        .flatMap(r => r.errors);  

    return errors.length === 0 ? Success(value) : Failure(errors);  
};  

// 具体验证器  
const isRequired = createValidator(  
    v => v !== null && v !== undefined && v !== '',  
    '此字段必填'  
);  

const isEmail = createValidator(  
    v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),  
    '请输入有效的邮箱地址'  
);  

const minLength = min => createValidator(  
    v => v && v.length >= min,  
    `长度至少为 ${min} 个字符`  
);  

const maxLength = max => createValidator(  
    v => !v || v.length <= max,  
    `长度不能超过 ${max} 个字符`  
);  

const isNumber = createValidator(  
    v => !isNaN(Number(v)),  
    '必须是数字'  
);  

const inRange = (min, max) => createValidator(  
    v => {  
        const num = Number(v);  
        return num >= min && num <= max;  
    },  
    `必须在 ${min} 到 ${max} 之间`  
);  

const matches = regex => createValidator(  
    v => regex.test(v),  
    '格式不正确'  
);  

// 验证整个表单  
const validateField = (fieldName, value, ...validators) => {  
    const result = combineValidators(...validators)(value);  
    return result.fold(  
        errors => ({ [fieldName]: { valid: false, errors } }),  
        value => ({ [fieldName]: { valid: true, value } })  
    );  
};  

const validateForm = schema => formData => {  
    const results = Object.entries(schema).map(([field, validators]) =>   
        validateField(field, formData[field], ...validators)  
    );  

    const merged = results.reduce((acc, r) => ({ ...acc, ...r }), {});  
    const isValid = Object.values(merged).every(r => r.valid);  

    return { isValid, fields: merged };  
};  

// 使用示例  
const userFormSchema = {  
    username: [isRequired, minLength(3), maxLength(20)],  
    email: [isRequired, isEmail],  
    age: [isRequired, isNumber, inRange(18, 120)],  
    password: [isRequired, minLength(8), matches(/[A-Z]/), matches(/[0-9]/)]  
};  

const validateUserForm = validateForm(userFormSchema);  

// 测试有效数据  
const validData = {  
    username: 'johndoe',  
    email: 'john@example.com',  
    age: '25',  
    password: 'SecurePass123'  
};  

console.log('有效数据验证:');  
console.log(validateUserForm(validData));  
// { isValid: true, fields: { username: { valid: true, value: 'johndoe' }, ... } }  

// 测试无效数据  
const invalidData = {  
    username: 'ab',  
    email: 'invalid-email',  
    age: '15',  
    password: 'weak'  
};  

console.log('\n无效数据验证:');  
const result = validateUserForm(invalidData);  
console.log('isValid:', result.isValid);  
Object.entries(result.fields).forEach(([field, data]) => {  
    if (!data.valid) {  
        console.log(`  ${field}: ${data.errors.join(', ')}`);  
    }  
});

8.3 状态管理

// 简易的函数式状态管理  
const createStore = (reducer, initialState) => {  
    let state = initialState;  
    const listeners = [];  

    return {  
        getState: () => state,  
        dispatch: action => {  
            state = reducer(state, action);  
            listeners.forEach(listener => listener(state));  
            return action;  
        },  
        subscribe: listener => {  
            listeners.push(listener);  
            return () => {  
                const index = listeners.indexOf(listener);  
                if (index > -1) {  
                    listeners.splice(index, 1);  
                }  
            };  
        }  
    };  
};  

// Action creators  
const createAction = type => payload => ({ type, payload });  

const actions = {  
    addTodo: createAction('ADD_TODO'),  
    toggleTodo: createAction('TOGGLE_TODO'),  
    removeTodo: createAction('REMOVE_TODO'),  
    setFilter: createAction('SET_FILTER')  
};  

// Reducer(纯函数)  
const initialState = {  
    todos: [],  
    filter: 'all', // 'all', 'active', 'completed'  
    nextId: 1  
};  

const todoReducer = (state = initialState, action) => {  
    switch (action.type) {  
        case 'ADD_TODO':  
            return {  
                ...state,  
                todos: [  
                    ...state.todos,  
                    { id: state.nextId, text: action.payload, completed: false }  
                ],  
                nextId: state.nextId + 1  
            };  

        case 'TOGGLE_TODO':  
            return {  
                ...state,  
                todos: state.todos.map(todo =>  
                    todo.id === action.payload  
                        ? { ...todo, completed: !todo.completed }  
                        : todo  
                )  
            };  

        case 'REMOVE_TODO':  
            return {  
                ...state,  
                todos: state.todos.filter(todo => todo.id !== action.payload)  
            };  

        case 'SET_FILTER':  
            return {  
                ...state,  
                filter: action.payload  
            };  

        default:  
            return state;  
    }  
};  

// Selectors(派生状态)  
const selectTodos = state => state.todos;  
const selectFilter = state => state.filter;  

const selectFilteredTodos = state => {  
    const todos = selectTodos(state);  
    const filter = selectFilter(state);  

    switch (filter) {  
        case 'active':  
            return todos.filter(t => !t.completed);  
        case 'completed':  
            return todos.filter(t => t.completed);  
        default:  
            return todos;  
    }  
};  

const selectStats = state => {  
    const todos = selectTodos(state);  
    return {  
        total: todos.length,  
        completed: todos.filter(t => t.completed).length,  
        active: todos.filter(t => !t.completed).length  
    };  
};  

// 使用  
const store = createStore(todoReducer, initialState);  

// 订阅状态变化  
const unsubscribe = store.subscribe(state => {  
    console.log('\n当前状态:');  
    console.log('Todos:', selectFilteredTodos(state));  
    console.log('统计:', selectStats(state));  
});  

// 派发 actions  
console.log('=== 添加待办事项 ===');  
store.dispatch(actions.addTodo('学习函数式编程'));  
store.dispatch(actions.addTodo('写代码'));  
store.dispatch(actions.addTodo('看文档'));  

console.log('\n=== 完成一项 ===');  
store.dispatch(actions.toggleTodo(1));  

console.log('\n=== 设置过滤器为 active ===');  
store.dispatch(actions.setFilter('active'));  

console.log('\n=== 删除一项 ===');  
store.dispatch(actions.removeTodo(2));  

// 取消订阅  
unsubscribe();

8.4 函数式工具库

// 创建一个小型函数式工具库  
const FP = {  
    // 核心函数  
    pipe: (...fns) => x => fns.reduce((acc, fn) => fn(acc), x),  
    compose: (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x),  

    curry: fn => {  
        const curried = (...args) =>  
            args.length >= fn.length  
                ? fn(...args)  
                : (...more) => curried(...args, ...more);  
        return curried;  
    },  

    // 数组操作  
    map: fn => arr => arr.map(fn),  
    filter: predicate => arr => arr.filter(predicate),  
    reduce: (fn, initial) => arr => arr.reduce(fn, initial),  
    find: predicate => arr => arr.find(predicate),  
    some: predicate => arr => arr.some(predicate),  
    every: predicate => arr => arr.every(predicate),  

    // 对象操作  
    prop: key => obj => obj[key],  
    assoc: (key, value) => obj => ({ ...obj, [key]: value }),  
    omit: keys => obj => {  
        const result = { ...obj };  
        keys.forEach(key => delete result[key]);  
        return result;  
    },  
    pick: keys => obj =>   
        keys.reduce((acc, key) => {  
            if (key in obj) acc[key] = obj[key];  
            return acc;  
        }, {}),  

    // 逻辑操作  
    not: fn => (...args) => !fn(...args),  
    and: (f, g) => (...args) => f(...args) && g(...args),  
    or: (f, g) => (...args) => f(...args) || g(...args),  

    // 条件操作  
    when: (predicate, fn) => x => predicate(x) ? fn(x) : x,  
    unless: (predicate, fn) => x => predicate(x) ? x : fn(x),  
    ifElse: (predicate, onTrue, onFalse) => x =>   
        predicate(x) ? onTrue(x) : onFalse(x),  

    // 比较操作  
    equals: a => b => a === b,  
    gt: a => b => b > a,  
    gte: a => b => b >= a,  
    lt: a => b => b < a,  
    lte: a => b => b <= a,  

    // 数学操作  
    add: a => b => a + b,  
    subtract: a => b => b - a,  
    multiply: a => b => a * b,  
    divide: a => b => b / a,  

    // 字符串操作  
    split: separator => str => str.split(separator),  
    join: separator => arr => arr.join(separator),  
    trim: str => str.trim(),  
    toLowerCase: str => str.toLowerCase(),  
    toUpperCase: str => str.toUpperCase(),  

    // 实用工具  
    identity: x => x,  
    constant: x => () => x,  
    tap: fn => x => { fn(x); return x; },  

    // 数组工具  
    head: arr => arr[0],  
    tail: arr => arr.slice(1),  
    last: arr => arr[arr.length - 1],  
    init: arr => arr.slice(0, -1),  
    take: n => arr => arr.slice(0, n),  
    drop: n => arr => arr.slice(n),  

    // 分组和排序  
    groupBy: keyFn => arr => arr.reduce((acc, item) => {  
        const key = keyFn(item);  
        acc[key] = acc[key] || [];  
        acc[key].push(item);  
        return acc;  
    }, {}),  

    sortBy: fn => arr => [...arr].sort((a, b) => {  
        const va = fn(a), vb = fn(b);  
        return va < vb ? -1 : va > vb ? 1 : 0;  
    }),  

    // 去重  
    uniq: arr => [...new Set(arr)],  
    uniqBy: fn => arr => {  
        const seen = new Set();  
        return arr.filter(item => {  
            const key = fn(item);  
            if (seen.has(key)) return false;  
            seen.add(key);  
            return true;  
        });  
    }  
};  

// 使用示例  
const { pipe, map, filter, reduce, prop, sortBy, take, groupBy } = FP;  

const users = [  
    { name: 'Alice', age: 25, department: 'Engineering' },  
    { name: 'Bob', age: 30, department: 'Marketing' },  
    { name: 'Charlie', age: 35, department: 'Engineering' },  
    { name: 'David', age: 28, department: 'Sales' },  
    { name: 'Eve', age: 32, department: 'Engineering' }  
];  

// 获取工程部门年龄最大的两个人的名字  
const result = pipe(  
    filter(user => user.department === 'Engineering'),  
    sortBy(prop('age')),  
    arr => arr.reverse(),  
    take(2),  
    map(prop('name'))  
)(users);  

console.log(result); // ['Charlie', 'Eve']  

// 按部门统计平均年龄  
const avgAgeByDept = pipe(  
    groupBy(prop('department')),  
    Object.entries,  
    map(([dept, members]) => ({  
        department: dept,  
        avgAge: members.reduce((sum, m) => sum + m.age, 0) / members.length,  
        count: members.length  
    })),  
    sortBy(prop('avgAge'))  
)(users);  

console.log(avgAgeByDept);

📖 总结

函数式编程的核心原则

  1. 使用纯函数 - 相同输入总是产生相同输出,无副作用
  2. 保持数据不可变 - 创建新数据而不是修改现有数据
  3. 使用高阶函数 - 函数可以作为参数传递和返回
  4. 函数组合 - 将简单函数组合成复杂函数
  5. 声明式编程 - 描述"做什么"而不是"怎么做"

学习路径建议

1. 基础阶段  
   ├── 理解纯函数概念  
   ├── 掌握 map、filter、reduce  
   └── 理解不可变性  

2. 进阶阶段  
   ├── 学习高阶函数模式  
   ├── 掌握函数组合和管道  
   └── 理解柯里化和偏应用  

3. 实践阶段  
   ├── 在项目中应用 FP 原则  
   ├── 使用 FP 库(Ramda、Lodash/fp)  
   └── 构建自己的工具函数库

推荐资源

记住:函数式编程不是全有或全无的选择。你可以在现有代码中逐步引入函数式概念,慢慢体会它带来的好处!

收起阅读 »

鸿蒙征文大赛获奖名单公示

鸿蒙征文

本次鸿蒙征文大赛收到大量优质投稿,开发者们积极分享了基于 uni-app 的鸿蒙开发实践经验,完整征文列表详见:鸿蒙征文

获奖名单

一等奖

获奖作者 获奖文章
yuhespace 【鸿蒙征文】从创业小白到省赛获奖:我们用 uni-app 做出了"校园人人帮"
d***@qq.com 【鸿蒙征文】 炸裂!我用uni-app三天让旧应用通杀鸿蒙Next+元服务,华为商店已上架!2W奖励金即将到账。
l***@163.com 精膳通智慧食堂的鸿蒙开发之旅:因 uni-app 而简化,为国产生态而让利
clearliu 从痛点到产品:uni-app x + HarmonyOS打造房产投资管理系统全记录
nutpi 从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器

二等奖

获奖作者 获奖文章
哦哦哦哈哈 从新建文件夹开始,用一个项目实战讲透uni-app鸿蒙开发到上架全流程(多图、细节)
小疯子呵 表情包搜索助手:uni-app 鸿蒙应用开发全流程解析
小白2023 低成本入局鸿蒙生态!Uniapp 适配鸿蒙实战分享,一次编码跑通多端
i***@alone88.cn 【鸿蒙征文】uni-app 鸿蒙开发实践:华为账号一键登录集成之路
GraceUI 【鸿蒙征文】关于鸿蒙折叠屏(宽屏设备)适配的一些分享
maq 鸿蒙企业应用内部分发打包教程
COOL团队 【鸿蒙征文】折腾鸿蒙分享功能的那些事儿
imseantang 【鸿蒙征文】从零到上架:用 uni-app 开发鸿蒙习惯养成应用习惯修仙的全流程实践
m***@163.com 一个人用 uni-app 做鸿蒙日语学习 App 的踩坑之旅
不如摸鱼去 uni-app 也能开发纯血鸿蒙 App?使用 wot-starter 这样快速上手!
陌上华年 【鸿蒙征文】UniApp(X) 让鸿蒙开发触手可及 —— LimeUI 组件库开发简录
tmui 【鸿蒙征文】记一次鸿蒙Next原生插件开发与Uniapp调用实战之弹层开发(tmui4x中的xToasts弹层)(含源码附件)
1***@qq.com 【鸿蒙征文】uniapp 实现鸿蒙自定义扫码界面
2***@qq.com 【鸿蒙征文】从鸿蒙适配到迎娶白富美:一个程序员的逆袭之路
VK168 【鸿蒙征文】uni-app鸿蒙上架必备技能:应用适配深色模式

三等奖

获奖作者 获奖文章
5***@qq.com 从uni-app到鸿蒙:爱影家影视App的跨平台开发实践
1***@qq.com 从Web到鸿蒙:uni-app x 开发踩坑与成长实录
诗酒趁闲 【鸿蒙征文】重拾儿时趣味的古诗 App 的开发过程记录
陌上华年 【鸿蒙征文】从零实现 uni-app 鸿蒙平台 TTS 插件:UTS 开发实践指南
知青 经验分享 鸿蒙里的权限设置,如何获取、查询权限
1***@qq.com 【鸿蒙征文】分享我 uniapp 集成鸿蒙企业微信的经验
暴走莫扎特 Uniapp 的鸿蒙 next 应用中隐藏和显示系统状态栏
正知名 【鸿蒙征文】从现在起,你的非原生弹窗"组件"们(自定义Toast、Modal等)只需要配置一次!
刘星 1024星光不负,码向未来——以 uni-app 筑梦鸿蒙生态我的uni-app鸿蒙开发之旅
青衫行者 【鸿蒙征文】星光不负,码向未来:uni-app + uniCloud 赋能社区管理系统的高效适配与生态融合实践
坚果派 摩尔斯电码转换器
小疯子呵 用uni-app搞了个足球战术板,踩了不少canvas坑,分享一下经验
VK168 【鸿蒙征文】uni-app 现有 UI 库兼容鸿蒙系统开发指南
3***@qq.com uni-app 打通鸿蒙从开发到上架:一条龙落地指南
程序媛夏天 使用 uni-app x 在 HarmonyOS 平台开发波斯历转换器的实践与思考
8***@qq.com 在 UniApp 中用 UTS 封装钉钉登录(HarmonyOS)
pickled 解决uniapp鸿蒙适配深色模式的问题
xxiaohe0601 【鸿蒙征文】Uni ECharts 2.1 发布:正式支持鸿蒙,零成本迁移、全平台兼容、跨端开发零负担!
Isaacedvr 鸿图初展,蒙学破茧:与uniapp-x和tmui4.0共闯鸿蒙Next实战录
Deminic 从零到一开发鸿蒙6原生时钟应用:uni-app x 完全实战指南
奇风2016 【鸿蒙征文】uniapp 赋能鸿蒙,应用开发速度翻倍,十天速通4个APP
i***@alone88.cn uni-app 鸿蒙应用开发实战:优雅解决文件下载存储路径问题
小疯子呵 【弧形导航栏】中间凸起按钮和消息未读角标,支持鸿蒙
8***@qq.com UniApp 项目鸿蒙上线
y***@163.com uniapp极速上手鸿蒙开发
8***@qq.com 鸿蒙 UTS 插件开发实战:屏幕方向控制插件的完整实现
用户2914143 使用 uni-app x 开发2048游戏适配鸿蒙6
威龙 【鸿蒙征文】使用 UTS 插件优雅实现"一键退出应用"功能
阿岳 【鸿蒙征文】使用 uni-app 开发鸿蒙 App,如何实现 H5 网页和 App 互通讯?
知青 鸿蒙 UTS 插件使用三方依赖、本地依赖

方向二特别奖

获奖作者 获奖文章
yuhespace 【鸿蒙征文】从创业小白到省赛获奖:我们用 uni-app 做出了"校园人人帮"
d***@qq.com 【鸿蒙征文】 炸裂!我用uni-app三天让旧应用通杀鸿蒙Next+元服务,华为商店已上架!2W奖励金即将到账。
imseantang 【鸿蒙征文】从零到上架:用 uni-app 开发鸿蒙习惯养成应用习惯修仙的全流程实践
iq2s 精膳通智慧食堂的鸿蒙开发之旅:因 uni-app 而简化,为国产生态而让利
clearliu 从痛点到产品:uni-app x + HarmonyOS打造房产投资管理系统全记录
5***@qq.com 从uni-app到鸿蒙:爱影家影视App的跨平台开发实践
m***@163.com 一个人用 uni-app 做鸿蒙日语学习 App 的踩坑之旅
8***@qq.com 集成鸿蒙五大核心能力,打造高性能生活服务类元服务
nutpi 从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器
诗酒趁闲 【鸿蒙征文】重拾儿时趣味的古诗 App 的开发过程记录

奖项设置

一等奖(5名)
如上奖品为二选一,随机发放;

  • 华为手环7-NFC版
  • 华为智能水杯450ml + 露营灯-无级调光-太阳能+Type-C充电 组合

二等奖(15名)
露营灯-无级调光,太阳能 或 Type-C充电,随机发货

三等奖(30名)
HUAWEI 无线蓝牙鼠标-双模办公-灰色 (价值99元)

方向二特别奖(10名)
针对【方向二·案例实战】的优质投稿,额外设立10份 华为手环 9 NFC版作为激励!


奖品领取

请各位获奖作者尽快提交自己的邮寄地址,我们会陆续联系获奖人员发放奖品。

邮寄地址提交方式:登录 ask社区,点击右上角个人头像,进入设置界面,设置界面下方补充快递邮寄地址。


感谢所有参与本次征文活动的开发者,你们的分享让uni-app和鸿蒙生态更加繁荣!

继续阅读 »

本次鸿蒙征文大赛收到大量优质投稿,开发者们积极分享了基于 uni-app 的鸿蒙开发实践经验,完整征文列表详见:鸿蒙征文

获奖名单

一等奖

获奖作者 获奖文章
yuhespace 【鸿蒙征文】从创业小白到省赛获奖:我们用 uni-app 做出了"校园人人帮"
d***@qq.com 【鸿蒙征文】 炸裂!我用uni-app三天让旧应用通杀鸿蒙Next+元服务,华为商店已上架!2W奖励金即将到账。
l***@163.com 精膳通智慧食堂的鸿蒙开发之旅:因 uni-app 而简化,为国产生态而让利
clearliu 从痛点到产品:uni-app x + HarmonyOS打造房产投资管理系统全记录
nutpi 从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器

二等奖

获奖作者 获奖文章
哦哦哦哈哈 从新建文件夹开始,用一个项目实战讲透uni-app鸿蒙开发到上架全流程(多图、细节)
小疯子呵 表情包搜索助手:uni-app 鸿蒙应用开发全流程解析
小白2023 低成本入局鸿蒙生态!Uniapp 适配鸿蒙实战分享,一次编码跑通多端
i***@alone88.cn 【鸿蒙征文】uni-app 鸿蒙开发实践:华为账号一键登录集成之路
GraceUI 【鸿蒙征文】关于鸿蒙折叠屏(宽屏设备)适配的一些分享
maq 鸿蒙企业应用内部分发打包教程
COOL团队 【鸿蒙征文】折腾鸿蒙分享功能的那些事儿
imseantang 【鸿蒙征文】从零到上架:用 uni-app 开发鸿蒙习惯养成应用习惯修仙的全流程实践
m***@163.com 一个人用 uni-app 做鸿蒙日语学习 App 的踩坑之旅
不如摸鱼去 uni-app 也能开发纯血鸿蒙 App?使用 wot-starter 这样快速上手!
陌上华年 【鸿蒙征文】UniApp(X) 让鸿蒙开发触手可及 —— LimeUI 组件库开发简录
tmui 【鸿蒙征文】记一次鸿蒙Next原生插件开发与Uniapp调用实战之弹层开发(tmui4x中的xToasts弹层)(含源码附件)
1***@qq.com 【鸿蒙征文】uniapp 实现鸿蒙自定义扫码界面
2***@qq.com 【鸿蒙征文】从鸿蒙适配到迎娶白富美:一个程序员的逆袭之路
VK168 【鸿蒙征文】uni-app鸿蒙上架必备技能:应用适配深色模式

三等奖

获奖作者 获奖文章
5***@qq.com 从uni-app到鸿蒙:爱影家影视App的跨平台开发实践
1***@qq.com 从Web到鸿蒙:uni-app x 开发踩坑与成长实录
诗酒趁闲 【鸿蒙征文】重拾儿时趣味的古诗 App 的开发过程记录
陌上华年 【鸿蒙征文】从零实现 uni-app 鸿蒙平台 TTS 插件:UTS 开发实践指南
知青 经验分享 鸿蒙里的权限设置,如何获取、查询权限
1***@qq.com 【鸿蒙征文】分享我 uniapp 集成鸿蒙企业微信的经验
暴走莫扎特 Uniapp 的鸿蒙 next 应用中隐藏和显示系统状态栏
正知名 【鸿蒙征文】从现在起,你的非原生弹窗"组件"们(自定义Toast、Modal等)只需要配置一次!
刘星 1024星光不负,码向未来——以 uni-app 筑梦鸿蒙生态我的uni-app鸿蒙开发之旅
青衫行者 【鸿蒙征文】星光不负,码向未来:uni-app + uniCloud 赋能社区管理系统的高效适配与生态融合实践
坚果派 摩尔斯电码转换器
小疯子呵 用uni-app搞了个足球战术板,踩了不少canvas坑,分享一下经验
VK168 【鸿蒙征文】uni-app 现有 UI 库兼容鸿蒙系统开发指南
3***@qq.com uni-app 打通鸿蒙从开发到上架:一条龙落地指南
程序媛夏天 使用 uni-app x 在 HarmonyOS 平台开发波斯历转换器的实践与思考
8***@qq.com 在 UniApp 中用 UTS 封装钉钉登录(HarmonyOS)
pickled 解决uniapp鸿蒙适配深色模式的问题
xxiaohe0601 【鸿蒙征文】Uni ECharts 2.1 发布:正式支持鸿蒙,零成本迁移、全平台兼容、跨端开发零负担!
Isaacedvr 鸿图初展,蒙学破茧:与uniapp-x和tmui4.0共闯鸿蒙Next实战录
Deminic 从零到一开发鸿蒙6原生时钟应用:uni-app x 完全实战指南
奇风2016 【鸿蒙征文】uniapp 赋能鸿蒙,应用开发速度翻倍,十天速通4个APP
i***@alone88.cn uni-app 鸿蒙应用开发实战:优雅解决文件下载存储路径问题
小疯子呵 【弧形导航栏】中间凸起按钮和消息未读角标,支持鸿蒙
8***@qq.com UniApp 项目鸿蒙上线
y***@163.com uniapp极速上手鸿蒙开发
8***@qq.com 鸿蒙 UTS 插件开发实战:屏幕方向控制插件的完整实现
用户2914143 使用 uni-app x 开发2048游戏适配鸿蒙6
威龙 【鸿蒙征文】使用 UTS 插件优雅实现"一键退出应用"功能
阿岳 【鸿蒙征文】使用 uni-app 开发鸿蒙 App,如何实现 H5 网页和 App 互通讯?
知青 鸿蒙 UTS 插件使用三方依赖、本地依赖

方向二特别奖

获奖作者 获奖文章
yuhespace 【鸿蒙征文】从创业小白到省赛获奖:我们用 uni-app 做出了"校园人人帮"
d***@qq.com 【鸿蒙征文】 炸裂!我用uni-app三天让旧应用通杀鸿蒙Next+元服务,华为商店已上架!2W奖励金即将到账。
imseantang 【鸿蒙征文】从零到上架:用 uni-app 开发鸿蒙习惯养成应用习惯修仙的全流程实践
iq2s 精膳通智慧食堂的鸿蒙开发之旅:因 uni-app 而简化,为国产生态而让利
clearliu 从痛点到产品:uni-app x + HarmonyOS打造房产投资管理系统全记录
5***@qq.com 从uni-app到鸿蒙:爱影家影视App的跨平台开发实践
m***@163.com 一个人用 uni-app 做鸿蒙日语学习 App 的踩坑之旅
8***@qq.com 集成鸿蒙五大核心能力,打造高性能生活服务类元服务
nutpi 从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器
诗酒趁闲 【鸿蒙征文】重拾儿时趣味的古诗 App 的开发过程记录

奖项设置

一等奖(5名)
如上奖品为二选一,随机发放;

  • 华为手环7-NFC版
  • 华为智能水杯450ml + 露营灯-无级调光-太阳能+Type-C充电 组合

二等奖(15名)
露营灯-无级调光,太阳能 或 Type-C充电,随机发货

三等奖(30名)
HUAWEI 无线蓝牙鼠标-双模办公-灰色 (价值99元)

方向二特别奖(10名)
针对【方向二·案例实战】的优质投稿,额外设立10份 华为手环 9 NFC版作为激励!


奖品领取

请各位获奖作者尽快提交自己的邮寄地址,我们会陆续联系获奖人员发放奖品。

邮寄地址提交方式:登录 ask社区,点击右上角个人头像,进入设置界面,设置界面下方补充快递邮寄地址。


感谢所有参与本次征文活动的开发者,你们的分享让uni-app和鸿蒙生态更加繁荣!

收起阅读 »

个人开发者承接热门打螺丝,挪车,三消换皮抖音微信小游戏,全职低价外包接单

游戏 外包接单

低价换皮包上线

有需要开发的并能看得上我的请联系我哈

vx:tbszjx

低价换皮包上线

有需要开发的并能看得上我的请联系我哈

vx:tbszjx

个人开发者承接app、小程序、网页外包,全职外包接单

uniapp 外包接单

全职在家承接外包,多年外包经验,个人开发者,绝对实惠靠谱,有很多款线上应用(度是自己开发的,自己独立完成,可查)

可做商城类,社交类,工具类,任务平台类,mes 类,扫码收银/分账交易类 等,除了游戏和带颜色的,其他度可以开发

可承接安卓/IOS、各个端的小程序、H5网页、PC网页开发,从前端到后端,全度会,一条龙服务,

可免费提供上架服务

掌握技术
前端:uniapp 、uniappx、vue
后端:Golang 、php

有需要开发的并能看得上我的请联系我哈

vx:wu1020yt

继续阅读 »

全职在家承接外包,多年外包经验,个人开发者,绝对实惠靠谱,有很多款线上应用(度是自己开发的,自己独立完成,可查)

可做商城类,社交类,工具类,任务平台类,mes 类,扫码收银/分账交易类 等,除了游戏和带颜色的,其他度可以开发

可承接安卓/IOS、各个端的小程序、H5网页、PC网页开发,从前端到后端,全度会,一条龙服务,

可免费提供上架服务

掌握技术
前端:uniapp 、uniappx、vue
后端:Golang 、php

有需要开发的并能看得上我的请联系我哈

vx:wu1020yt

收起阅读 »