✨
blog
  • Blog
  • Element-UI
    • 2019-09-04
  • JS
    • ES6 之 Set 和 Map
    • let 和 const 声明常见概念
    • 元编程
    • ES6之字符串的扩展
    • ES6 之异步流程的前世今生(上)
    • ES6之异步流程的前世今生(下)
    • ES6 之模块你知吗
    • ES6 之解构赋值与箭头函数的妙用
    • 迭代器
    • ES5 之原型(一)
    • ES6之类(二)
    • es7之装饰器
    • es6之数组详解
    • js之this指向
    • 对象
    • vue项目配合使用canvas联动
    • 本文解决痛点:对象里面是否有值
  • MAC
    • vue源码之method
    • Mac的使用技巧
    • 前文
    • Mac常用软件(二)
    • 如何查看 Mac 端口号以及占用情况
  • Node
    • Node之Buffer详解
    • 浏览器与 node 的事件循环(event loop)有何区别
    • Node之多线程
    • node之模块解析(一)
    • 错误捕获与内存告警
  • TS
    • Record
    • 使用方法
    • 工具泛型
    • 类型体操
    • 泛型
  • chrome
    • v8 引擎
    • v8 垃圾回收机制
    • 浏览器的知识
  • flutter
    • 路由
    • 页面布局
  • go
    • index
  • html&css
    • 两栏布局
    • ES5和ES6的区别
    • ES5 和 ES6 的区别
    • HTTP详解
    • TCP 与 UDP 的区别
    • MDN
    • css modules 使用教程
    • css 居中
    • 拖拽
    • flex布局
    • h5 新增特性 html5
    • history 与 hash 路由策略
    • position 定位方式
    • rem布局
    • svg
    • web性能优化
    • 事件循环
    • 从输入网址后发生了什么
    • 前端状态管理
    • 圣杯布局与双飞翼
    • 性能优化 页面的性能统计指标
    • 本地存储的几种对比
    • 浏览器的渲染进程
    • 浏览器缓存策略详解
    • 盒模型
    • 为什么要移动端适配
    • 跨域的 N 种实现方式
  • web3
    • 常见概念
    • vue项目配合使用canvas联动
  • webgl
    • Mac使用技巧(二)
    • Node之模块解析path
  • 代码库
    • documeng的一些常见操作
    • eventBus事件
    • jquery提交
    • jquery的一些常见操作
    • 常见操作
    • 数组polyfill
    • TS代码片段
      • 面试官眼中的test unit
  • 全年安排
    • AfterShip
    • 大企业
  • 函数编程题
    • Promise问题
    • 继承
  • 前端早早聊
    • vue生态
    • 开发一款VScode语言插件
    • 简历回顾和进行复盘
    • 重新认知性能优化及其度量方法
    • 2022-09-17-音视频专场.md
      • 2022-09-17-音视频专场
    • 前端晋升专场
      • 成长的诀窍是靠自己
      • 销销帮
    • 前端监控专场
      • 字节前端监控实践
      • 李港:大前端-从无到有搭建自研前端监控系统
    • 前端跳槽
      • 50个面试官在线招聘
      • 如何识别优秀的前端猎头来跳槽大厂
      • 面试套路
    • 支付宝
      • 面试
    • 管理专场
      • 芋头:管理者眼中的web技术发展前沿
    • 组件专场
      • 基于webCompents的跨技术组件库实践
    • 面试
      • 面试辅导问题
      • 早早聊面试
      • 前端沙箱是什么? 怎么实现沙箱环境?
  • 常见总结
    • 2018年终总结-年底了,你总结了吗?我先来
    • 在逆境中成长
    • 2021年终总结
    • 2024年全年总结
    • 项目
    • Tell2.0 前端复盘
    • 复盘
    • 前端工程师素养
    • 学习方法论
    • 希望与破晓| 2022 年终总结
    • 新起点, 新征途 | 掘金年度征文
    • 稳定| 2023 年终总结
    • 趁着有风快飞翔 | 2019 年终总结
    • AfterShip
      • Emotion:用 JavaScript 编写 CSS 的强大工具
      • 个人中长期目标
      • 事故复盘
      • 时间解析
      • 国内外区别
      • 独立站建设
    • MEIZU
      • NativeApp与H5通信原理
      • SSR 原理
      • SSR的常见问题
      • CLI
      • electron 应用发布流程
      • electron
      • electron 面试
      • 数据结构与算法之美
      • mgc 一期复盘
      • 架构原理
      • 喵币管理
      • 三期复盘总结
      • 异常监控之 sentry 实践
      • 微前端
      • qiankun 原理解析
      • 快游戏一期
      • 游戏中心复盘
    • 个人准则
      • index
    • 编程猫
      • pc 接入 micro bit 方案
      • prompt engineer
      • web work 跨域解析与解决方式
      • web 中的 ai
      • 低版本 node 环境下 ffmpeg 的使用
      • 关于 taobao 源 https 过期
      • 加密 json
      • 安卓 5 和 6 的白屏解决
      • 性能排查与优化实践
      • 探月接入
      • 接入硬件
      • 新生态下的state
      • monorepo 包管理方式
      • 自修复 npm 库
      • 音频的绘制
    • 谨启
      • 音视频
      • 小程序
        • taro 规范
        • 结合 mobx 在跳转前预请求
        • Taro 浅析用法与原理
        • 前文
        • 小程序优化指南
        • 小程序内部实现原理
        • 支付相关
    • tencent
      • TAPD
        • MathJax的食用
        • canvas渲染优化策略
        • 为什么 JavaScript 是单线程的呢?
        • svg 总是不对
        • 前端库
        • 原生端和js端如何通信
        • 在旧项目中复用vue代码
        • 提升自我
        • 批量编辑优化
        • 插入业务对象
        • 编辑器
        • 挂载点
        • 性能优化对比
        • 遇到的问题
        • 项目迁移公告
        • 领导力
      • 行家
        • 实战篇
        • 职业发展、领导力、个人成长
        • 高质量沟通
  • 慕课网
    • react-native原理
    • react-native学习
  • 杂文
    • Dom 节点变动检测并录制的简单实现
    • 错误监控&错误捕获
    • NextJS与NuxtJS
    • 负载均衡的几种常用方式
    • PM2
    • service worker 控制网络请求?
    • SSL 和 TLS 的区别
    • Babel 你太美
    • echart踩坑经验
    • keyup、keydown你都知道有什么区别吗
    • 常见概念
    • 首屏加载优化与性能指标分析
    • preload 和 prefetch 的详解
    • 在项目中配置这几个关系
    • roullp 解析
    • tinymce原理浅析
    • wasm 在前端的应用
    • websocket
    • webworker
    • 项目
    • 从 ajax 到 axios
    • 从postcss 到自己开发一款插件
    • 从输入浏览器到页面展示涉及的缓存机制
    • 代码整洁之道
    • 你知道什么是aop吗
    • 函数式编程
    • 函数式编程指南
    • 前端input框文字最大值
    • 攻坚战
    • 前端书写 sdk
    • 前端文字转语音播放
    • 前端领域的 Docker 和 Kubernetes
    • 前端安全
    • 前端进阶之内存空间
    • 前端音频浅析
    • 十分钟搞定多人协作开发
    • 字符串的比较
    • 尾递归
    • 前文
    • 常见的算法可以分为以下三类
    • 手机调试--mac篇
    • 数组的原生系列
    • COOP 和 COEP - 新的跨域策略
    • 浅谈react组件书写
    • 浏览器与 Node.js 事件循环的区别
    • 由三道题引伸出来的思考
    • 移动端300ms点击延迟
    • 移动端和pc端事件
    • Git 常见疑惑
    • 我们离发 npm 包还有多远
    • 重绘和重排
    • AI 时代下的前端编程范式
    • 音频可视化实战
  • 极客时间
    • Serverless入门课
    • 二分查找
    • 二叉树
    • 全栈工程师
    • 动态规划面试宝典
    • 前端与rust
    • 散列表
    • 前端方面的Docker和Kubernetes
    • 栈
    • 深入浅出区块链
    • 玩转 vue 全家桶
    • 玩转 webpack
    • 程序员的个人财富课
    • 算法
    • 说透元宇宙
    • 跳表
    • 链表
    • 10x 程序员工作法
      • index
    • Node开发实战
      • HTTP服务的性能测试
      • JavaScript语言精髓与编程实战
      • 什么是node。js
      • svg精髓
    • ReactHooks核心原理与实战
      • ReactHooks核心原理与实战
    • Rust
      • Rust编程第一课
      • 前置篇
      • 深度思维
      • 重构
      • 类型体操
      • 基础知识
    • WebAssembly入门课.md
      • 基础篇
      • SSR的注水和脱水
      • jsBriage通信原理
      • 基础知识篇
    • 互联网的英语私教课
      • 互联网人的英语私教课
    • 代码之丑
      • 代码之丑
    • 前端全链路优化实战课
      • 网页指标
    • 图解 Google V8
      • 图解 Google V8
    • 浏览器工作原理与实践
      • 浏览器工作原理与实践
    • 算法面试通关 40 讲
      • 算法面试通关40讲
    • 跟月影学可视化
      • index
    • 软件设计之美
      • 软件设计之美
    • 重学前端
      • js
  • 后续的文件增加都会增加到上面并以编号对应
    • 1029. 两地调度
    • 151.翻转字符串里的单词
    • 2022.3.15
    • 前端数据结构
    • 前端常见算法
    • 前端常见排序
    • 恢复一棵树
  • 设计模式
    • 前端常见设计模式之MVC与MVVM
    • 前端之代理模式
    • 前端常见设计模式之单例模式
    • 前端常见设计模式之发布订阅模式
    • 前端之工厂模式
    • 观察者模式
    • 前端常见设计模式之适配器模式
  • 译文
    • [译] 如何使用CircleCI for GitHub Pages持续部署
    • 您是否优化了 API 的性能
    • [译][官方] Google 正式发布 Flutter 1.2 版本
    • 什么是 Deno ,它将取代 NodeJS ?
  • 读后感
    • JavaScript二十年
    • 1368个单词就够了
    • js编程精解
    • labuladong 的算法小抄
    • lodash常用方法
    • vue的设计与实现
    • 所有的静态资源都是get请求
    • 人生
    • 人生护城河
    • 你不知道的JavaScript
    • 前端核心知识进阶
    • 华为工作法
    • 反脆弱
    • 好好学习
    • 左耳听风
    • 摩托车维修之道
    • 数学之美
    • 深入理解svg
    • 浏览器的ESM到底是啥
    • 经济学原理
    • 编程珠玑
    • 防御式 css 精讲
    • 韭菜的自我修养
  • 雪狼
    • 2022-07-17
    • 基础知识
    • 阶一课程
      • 实战辅导一
      • 实战辅导二
  • 嵌入式
    • 树莓派
      • 排序
  • 源码
    • React
      • 核心知识点
      • errorBoundaries
      • immutable.js 的实现原理
      • React.Suspense
      • react源码分析之Fiber
      • batchedUpdate
      • Component
      • Context
      • react 源码分析之 diff 算法
      • React 中的 key 属性:原理、使用场景与注意事项
      • 使用方式
      • react源码分析之memo
      • react 源码分析之mixin
      • 实战篇
      • react源码分析之react-dom
      • 使用方式
      • scheduleWork
      • useImperativeHandle的使用与原理
      • React 书写小技巧
      • 入口和优化
      • 合成事件和原生事件的区别
      • react 性能优化
      • 构建一个 hooks
      • 浅析 styled-components
      • 生命周期
      • 组合 vs 继承
      • 通信机制
      • 高阶组件
      • 慕课网
        • 应用篇
        • 课程导学
    • ReactHook
      • useCallback
      • useContext
      • useEffect 与 useLayoutEffect
      • useHook
      • useMemo
      • useReducer
      • 原理
      • useState
      • 总结
    • Redux
      • mobx 原理解析
      • redux-saga
      • redux-thunk
      • Mobx 和 Redux 对比
      • 使用方法
      • redux 原理
    • Vite
      • Vite原理
      • Vite配置
      • 热更新原理
      • vite 为什么生产环境用 Rollup
    • Webpack
      • PostCSS
      • Webpack5 核心原理与应用实践-loader
      • Webpack5 核心原理与应用实践-plugin
      • Webpack5 核心原理与应用实践
      • 区分
      • 升级详情
      • treeShaking(树摇Tree Shaking)
      • 编写一个自己的webpack插件plugin
      • 代码分离(code-splitting)
      • webpack 打包优化
      • 基础配置
      • webpack 打包优化
      • webpack 工作原理
      • webpack 按需加载原理
      • webpack 热更新 HMR(Hot Module Replacement)
      • 缓存
      • webpack 自定义 plugin
    • next
      • tailwind
      • 什么是水合
    • sveltejs
      • index
    • tinymce
      • 并发篇
    • 源码手写系列
      • create
      • call
      • bind
      • call
      • es6 单例
      • forEach vs Map
      • instanceOf
      • new
      • reduce
      • 取两个重复数组的交集
      • 函数柯理化
      • 动态规划
      • 基于Generator函数实现async
      • 新建 js 文件
      • 手写一个 slice 方法
      • 手写一个 webpack loader
      • Plugin
      • 手写一个寄生组合式继承
      • 二叉树
      • 链表相关的操作
      • 手动实现发布订阅
      • 数组去重
      • 数组扁平化
      • 数组
      • 构造大顶堆和小顶堆
      • 深浅拷贝 深拷贝
      • 两者对比
    • vue
      • vue2
        • vm.attrs与$listeners
        • vue 和 react 的 diff 算法比较
        • vue 源码分析
        • vue 优化的 diff 策略
        • extends
        • 核心原理篇
        • keep-alive
        • vue 源码分析之 mixins
        • vue 源码分析之 nextTick
        • vue之slot
        • vnode
        • vue 源码分析之 watch
        • 原理
        • vue 源码分析之transition
        • vue 源码分析之异步组件
        • 调用的是 watch
        • 安装
        • react源码分析之portals
        • event 的实现原理(事件的实现原理)
        • 什么是h
        • 分析provide 和 inject
        • vue 源码分析之 use
        • v-model
        • vue源码分析之vuex
        • 响应式原理
        • 初始化的流程
        • 组件更新
        • 编译
        • 父子组件生命周期
        • 原理
        • 多实例
        • Vue 面试
        • 源码研读一
        • 响应式原理
        • 常见问题
        • 数组的劫持
        • vue之自定义指令
        • 运行机制全局概览
      • vue3相比vue2的提升点
        • vue composition api
        • vue3的虚拟dom优化
        • vue3层面的双向数据绑定
        • 预处理优化
  • 重构
    • notification
      • 讲解
  • 面试
    • AfterShip经历
      • JS对URL进行编码和解码
      • ShippingLabelTemplate
      • 接入keycloak详解
      • reCAPTCHA接入
      • yalc与动态解决升级的依赖包
      • RBAC 简介
      • 多语言计划
      • 接入Google登录及其主动弹出快捷登录方式
      • 读书计划
        • 传染
        • 这就是OKR
    • 编程猫经历
      • 2024.1.16
      • 2025.2.20
      • 2025.2.21
      • 2025.2.26
      • 2025.3.28
      • 2025.3.3
      • 2025.3.7
      • 行动轨迹
      • 面试主观题
    • 腾讯经历
      • 2022.02.21
      • 2022.03.30
      • 2022.04.24
      • 2022.04.25
      • 2022.04.27
      • 2022.04.28
      • 2022.04.29
      • 2022.05.05
      • 不同公司的面试关注点不同
      • 2022.05.07
      • 2022.05.09
      • 2022.05.10
      • 2022.05.11
      • 2022.05.12
      • 2022.05.13
      • 2022.05.16
      • 2022.05.17
      • 2022.05.19
      • 2022.05.27
      • 面试
      • 行动轨迹
      • 面试主观题
    • 针对字节
      • 2022.05.14
      • 2022.05.17
      • HR面试准备
      • Promise的相关题目
      • React 进阶实践指南(二)
      • React 面试准备
      • vue 与 react 有什么不同 (react 和 vue 有什么区别)
      • TypeScript 全面进阶指南
      • cookie和session区别
      • express 面试准备 koa 中间件原理
      • next面试准备
      • requestCallBack
      • interface 与 type 异同点
      • 取消 promise
      • 如何设计一个前端项目
      • 进阶篇
      • 早早聊面试准备
      • 自动化部署
      • 挖掘项目的深度
      • 面试
      • 出题指数
    • 魅族经历
      • 2020.09.11
      • 一灯
      • 一灯
      • 一灯
      • 2020.09.20
      • 2020.09.21
      • 网易二面
      • 2020.09.23
      • 头条
      • 360 金融面试题
      • 富途一面
      • 算法
      • 字节
      • 2020.11.04
      • baidu 一面
      • meta 标签的作用
      • 字节
      • 2020.11.22
      • 2020.11.25
      • 微前端接入笔记
      • 面试的基本原则
由 GitBook 提供支持
在本页
  • 函数式编程指南
  • 是否遵循以下原则
  • Num => Math.pow(num, 2)
  • 作为匿名函数或 landama 表达式给变量赋值
  • 作为成员方法给对象的属性赋值
  • 闭包和作用域

这有帮助吗?

  1. 杂文

函数式编程指南

函数式编程指南

是否遵循以下原则

可扩展性 易模块化 可重用性 可测性 易推理行

函数式编程的一些概念

声明式编程 纯函数 引用透明 不可变性

使用函数式来解决 array.map()只需要对应用在每个数组元素的行为予以关注,将循环交给系统的其他部分去空

[1, 1, 2, 3, 4].map(function (num) {
  return Math.pow(num, 2);
});

将 es6 的 lambda 表达式以及箭头函数的优势来将循环抽象成函数

Num => Math.pow(num, 2)

Function(num){
return Math.pow(num, 2);
}

函数式编程旨在尽可能的提高代码的无状态性和不变性

函数式编程

  1. 仅取决于提供的输入,而不依赖于任何在函数期间或调用期间间隔时可能变化的隐藏状态和外部状态

  2. 不会造成超出其作用域的变化, 例如修改全局对象或引用传递的参数

我们习惯了命令式 声明一个状态变为另一个状态的变量

常见副作用:

  1. 改变一个全局的变量属性或者数据结构 2.改变一个函数参数的原始值 3.处理用户输入 4.抛出一个异常,除非它又被当前函数捕获了 5.屏幕打印或记录日志 6.查询 html 文档、浏览器的 cookie 或访问数据库

常见的函数式编程技巧—- curry 科里化

确保一个函数有相同的返回值是一个优点, 引用透明

引用透明 又称为等式正确性

array.sort 函数是有状态的,会导致在排序过程中产生副作用,因为原始的引用被修改的了。

强迫自己去思考纯的操作,将函数看作永远不会修改数据的闭合功能单元,必然可减少这种潜在的 bug 的可能性

使用流式链来处理

函数链是一种惰性计算程序, 这意味着需要时才会执行。

学习响应式编程的第一部分就是学习函数式编程, 这种编程范式使用了一个叫做 observable 的概念,observable 能够订阅一个数据流,让开发者可以通过使用组合和链式操作优雅的处理数据

尽管 js 是面向对象的,但其并不具备 java 那样的语言中典型的继承关系。

面向对象的核心,就是将创建派生对象做为程序中代码重用的主要手段。

特加一些额外的功能。同对象的核心,就是将建派生对象作为程序中代码重用的主要手段。因此, CollegeStudent 将重用从其父类继承而来的所有数据与行为。但这使得在原对象中添加更多功能变得很棘手,因为它的后代们并不一定会适用于这些新功能。虽然 firstname 和 1 astname 适用于 Person 及其所有的子类型,但可以说让 workAddress 作为(从 Person 派生的) Employee 对象的一部分比起 Student 对象要更合理一些。之所以举样的例子,是为了解释在数据(对象属性)与行为(函数)的组织上,面向对象和函数式的主要差别。

面向对象的应用程序大多是命令式的,因在很大程度上依赖于使用基于对象的封装来保护其自身和继承的可变状态的完整性,再通过实例方法来暴露或修改这些状态。其结果是,对象的数据与其具体的行为以种内聚的包裹的形式紧耦合在一起。而这就是面向对象程序的目的,也正解释了为什么对象是抽象的核心。 再看函数式编程,它不需要对调用者隐藏据,通常使用一些更小且非常简单的数据类型。由于一切都是不可变的,对象都可以直接拿来使用的,而且是通过定义在对象作用域外的函数来实现的。换句话说,数与行为是松耦合的。正如在图 2.1 中看到的,函数式代码使用的是可以横切或工作于多种数据类型之上的更加粗粒度的操作,而不是一些细粒度的实例方法。在这种范式中,函数成为抽象的主要形式。 如图 2.1 所示,两种范式的差别随着横竖标的增长逐渐显现。在实践中,一些极好的面向对象代码均使用两种编程范式正是在这个相交的平衡点上。要做到这一

把对象视为不可变的实体或值,并将他们的功能拆分可应用在该对象上的函数 函数成为抽象的主要形式

// ex1
const res = data && data.length > 0 && data.map(...)
// ex2
const backgroundImage = state.popperImageURL
  ? state.popperImageURL.replace(...)
  : null

实例方法使用 this 访问对象的数据,这会产生副作用

将对象作为参数消除了 this 的使用,也消除了副作用

面向对象的关键是创建继承层次结构,并将方法与数据紧密的绑定在一起,函数式编程则更倾向于通过广义的多态函数交叉应用于不同的数据类型,同时避免使用 this

exports.Person = class Person {
  constructor(ssn, firstname, lastname, birthYear = null, address = null) {
    this._ssn = ssn;
    this._firstname = firstname;
    this._lastname = lastname;
    this._birthYear = birthYear;
    this._address = address;
  }

  get ssn() {
    return this._ssn;
  }

  get firstname() {
    return this._firstname;
  }

  set firstname(firstname) {
    this._firstname = firstname;
    return this;
  }

  get lastname() {
    return this._lastname;
  }

  get birthYear() {
    return this._birthYear;
  }

  get address() {
    return this._address;
  }

  get fullname() {
    return `${this._firstname} ${this._lastname}`;
  }
};

exports.Student = class Student extends Person {
  constructor(
    ssn,
    firstname,
    lastname,
    school,
    birthYear = null,
    address = null
  ) {
    super(ssn, firstname, lastname, birthYear, address);
    this._school = school;
  }

  get school() {
    return this._school;
  }
};

尽管 js 的原始类型不可改变,但引用原始类型的变量状态是可以被更改的。

封装是一个防止篡改的不错策略。 值对象模式, 值对象是指其相等性不依赖于标识或引用,而只基于值,一旦声明,其状态可能不会在改变

function zipCode(code, location) {
  let _code = code;
  let _location = location || "";
  return {
    code: function () {
      return _code;
    },
    location: function () {
      return _location;
    },
    fromString: function (str) {
      let parts = str.split("-");
      return zipCode(parts[0], parts[1]);
    },
    toString: function () {
      return _code + "-" + _location;
    },
  };
}

使用函数来保障 code 的内部访问权限, 通过返回一个对象字面接口来 公开一小部分方法给调用者,这个样就能够将_code_location 视为伪私有变量 只能通过闭包的方式由对象的字面定义中访问。

让方法返回一个新的副本是另一种实现不可变性的方式,在该对象上应用一次平移将产生一个新的 coordinate 对象

值对象是一个由函数式编程启发来的面向对象设计模式

object.freeze 机制 是一种浅冻结

我们可以使用递归函数来深冻结对象

var isObject = (val) => val && typeof val === "object";

function deepFreeze(obj) {
  if (isObject(obj) && !Object.isFrozen(obj)) {
    // 跳过已经冻结过的对象,冻结没有被冻结过的对象
    Object.keys(obj).forEach((name) => deepFreeze(obj[name]));
    Object.freeze(obj); // 冻结根对象
  }
  return obj;
}

module.exports = {
  deepFreeze: deepFreeze,
};

lenses 也被称为函数式引用, 从本质上是函数,是程序设计中用于访问和不可改变的操作状态,数据类型属性的解决方案,从本质上讲,lenses 与写实复制策略的工作方式类似。即采用一个能够合理管理和复制状态的内部存储部件

r.view(last);

函数只有在返回一个有价值的结果,而不是 null 或者是 undefined 时才有意义,反之它就会更改外部数据并产生副作用,为了达到学习目的,我们需要区分表达式(如返回一个值的函数)和语句(如不返回值的函数)命令式编程和过程式编程,大多是由一系列有序的语句组成,而函数式编程完全依赖于表达式,因此无知函数在该范式下并没有意义

一等函数

作为匿名函数或 landama 表达式给变量赋值

var square = x => x \* x;

作为成员方法给对象的属性赋值

var obj = {
  method: function (x) {
    return x * x;
  },
};

构造函数已函数形参, 函数体作为参数,并需要使用 new 关键字

var multiplier = new Function("a", "b", "return a * b");
multiplier(2, 3);

在函数都是 Function 类型的一个实例, 函数的 length 属性可以用来获取形参的数量,而像 apply 和 call 方法可以用来调用函数并加入上下文

匿名函数表达式的右侧是一个具有真空 name 属性的函数式对象。 可以通过匿名函数作为参数的方式

function applyOperation(a, b, opt) {
  return opt(a, b);
}

forEach 是函数式推荐的循环方式

通过使用高阶函数,开始呈现出声明式

作为构造函数与 new 一起使用 -- 这种方式会返回新创建对象的引用

function MyType(arg) {
  this.prop = arg;
}

var someVal = new MyType("some argument");
function negate(func) {
  return function () {
    return !func.apply(null, arguments);
  };
}

function isNull(val) {
  return val === null;
}

let isNotNull = negate(isNull);
assert.ok(!isNotNull(null)); //-> false
assert.ok(isNotNull({})); //-> true

apply 接受一个参数组成的数组,而后者接收参数列表, 第一个参数 thisArg 可用于按需修改函数的上下文,他们的函数签名如下:

Function.prototype.apply(this.arg, [argsArray]);
Function.prototype.call(this.arg, arg1, arg2);

通过 thisArg 修改函数上下文可以灵活, 如果 this.arg 是一个对象,它表示该函数将对象的成员方法被调用。如果 this.isArg 为 null, 则表示 该函数的上下文为全局对象, 该函数的行为就像一个全局函数。 通过 thisArg 修改函数上下文可以灵活地应用在许多不同的技术中。

尽管全局共享以及对象上下文在函数式 js 编程中没有太大用处,函数上下文。

闭包和作用域

闭包是一种能够在函数声明过程中,将环境信息与所属函数绑定在一起的数据结构。它是基于函数声明的文本位置的,因此也被称为围绕函数定义的静态作用域或词法作用域。

闭包不仅应用于函数式编程的高阶函数中,也可用于事件处理和回调中.

从本质上讲, 闭包就是函数继承而来的作用域。这类似于对象方法是如何访问其继承的实例变量的, 它们都具有父类型的引用。

var outerVar = 'Outer';
 function makeInner(params){
     var innerVar = 'Inner';
     function inner(){
         console.log(`I can see: ${outerVar}, ${innerVar}, and ${}`)
     }
     return inner;
 }

var inner = makeInner('Params');
inner();

闭包的实际应用

  1. 模拟私有变量

  2. 异步服务端调用

  3. 创建人工块作用域变量

闭包还可以用来管理的全局命名空间,以免在全局范围内共享数据,一些库和模块还会使用背包来隐藏整个模块的所有方法和数据,这被称为模块模式,它采用了立即调用函数表达式。

全局作用域、函数作用域、伪块作用域

变量名称解析与之前所述的原型名称解析链非常相似。

  1. 首先检查变量的函数作用域

  2. 如果不是在局部作用域, 那么逐层向外检查各词法作用域,搜索该变量的引用,直到全局作用域。

  3. 如果无法找到变量引用,那么 JavaScript 将返回 undefined

将所有的功能代码包裹在良好封装的模块之中是一个通用的最佳实践 给 iife 一个名字, 这样有用的信息更方便栈跟踪

使用函数是开发风格,操作数据结构其实就是将数据控制流是为一些高级组件的连接。

方法链是一种能够在一个语句中调用多个方法的面向对象编程方式。但这些方法属于同一个对象时,里又称为方法级联?

方法链模式

"Functional Programming".substring(0, 10).toLowerCase() + " is Fun";

函数式

concat(toLowerCase(substring("Functional Programming", 0, 10)), "is fun");
只要遵守不可变的原则, 函数式中也会应用这种隶属于单个对象实例的方法链

函数链

接受函数作为参数,以便能够注入替代解决特定任务的特定行为 代替冲充斥着类似变量与副作用的传统循环结构,从而减少所要维护以及可能做错的代码。

lambda 表达式适用于函数式的函数定义,因为它总是需要返回一个值。

函数名代表的不是一个具体的值而是(一种惰性计算的),可获取其值得描述, 换句话说它说明指向的是代表着如何计算该数据的箭头函数,就是在函数式编程中可以将函数作为数值使用的原因。

_.map(node.children, function (child) {
  Tree.map(child, fn, tree);
});

map 是一个只会从左往右遍历的操作,对于从右到左的遍历,必须先反转数组。 js 的 array.reverse()操作是不能用在这里使用的,因为他会改变原数组。可以将 lodash 中功能等价的 reverse 操作与 map 连接起来

_(person)
  .reverse()
  .map((p) => (p !== null && p !== undefined ? p.fullName() : ""));

容器的映射

_.reduce 收集结果

高阶函数 reduce 将一个数组中的元素精简为单一值,该值由每一个元素与一个累积值通过一个函数计算得出的。

map 和 reduce 会遍历整个数组

  • 实现 map

  • 实现 reduce

_.filter 删除不需要的元素

filter 是一个能够遍历数组种的元素并返回一个新子集数组的高阶函数, 其中的元素有谓词函数 p 计算得出的 true 值结果来决定

数组推导式和列表推导式

代码推理: 动态部分包括所有变量的状态和函数的输出, 静态部分包括可读性以及设计的表达水平

声明式惰性计算函数链

类 sql 的数据:函数即数据

这个函数包含以下两个主要部分 基例,也称之为终止条件,基地是能够令递归函数计算出具体结果的一种输入,而不必再重复下去, 递归条件则处理函数调用自身的一种输入必须小于原始值,如果速度不行不变小于就会无限的循环支持,程序崩溃。

es6 的尾调用优化

在函数式编程中,函数式输入和输出类型之间的数学映射

类型--函数的返回类型必须与接收函数的参数类型相匹配。 元数-接收函数必须声明至少一个参数才能处理上一个函数的返回值。

函数与元数: 元组的应用

元数定义为函数所接收的参数数量,也被称为函数的长度。

只有单一参数的纯函数是最简单的,因为其实现目的的非常单纯,意味着职责非常单一

函数式语言通过一个称为元组的结构来做到这一点类型--函数的返回类型必须与接收函数的参数类型相匹配。 元数-接收函数必须声明至少一个参数才能处理上一个函数的返回值。

但如何返回两个不同的值呢?函数式语言通过一个称为元组的结构来做到这一点元组是有限的、有序的元素列表,通常由两个或三个值成组出现,记为(a,b,c)。由此,可以使用一个元组作为 isValid 函数的返回值--它将状态与可能的错误消息捆绑,作为单个实体返回,并随后传递到另一个函数中(如果需要的话)。下面详细探讨 一下元组。 元组是不可变的结构,它将不同类型的元素打包在一起,以便将它们传递到其他压数中。将数据打包返回的方式还包括字面对象或数组等: return i status : false or return [false, 'Input is too long!'! message: 'Input is too long!"

但当涉及函数间的数据传输时,元组能够具有更多的优点。 不可变的--一旦创建,就无法改变一个元组的内部内容。 避免创建临时类型--元组可以将可能毫不相关的数据相关联。而定义和实例化一些仅用于数据分组的新类型使得模型复杂并令人费解。 避免创建异构数组--包含不同类型元素的数组使用起来颇为困难,因为会导致代码中充满大量的防御性类型检查。传统上对象。

在缺少参数的情况下调用非柯里化函数会导致缺失参数的实参变成 undefined

柯里化是一种在所有参数被提供之前挂起或延迟函数执行,将多参数函数转化为一元函数序列的技术

由于 JavaScript 本身不支持自动柯里化函数,因此需要编写一些代码来启动它

柯里化是一种词法作用域,其返回的函数,只不过是一个接受后续参数的简单嵌套函数包装器

function curry(fn) {
  return function (firstArg) {
    return function (secondArg) {
      return fn(firstArg, secondArg);
    };
  };
}

const name = (curry(function (lats, first) {
  return new StringPair("Barkley", "Rosser");
})[(first, last)] = name("Curry")("Haskll").values());

当完整的传递两个参数是,函数会完全求值

当提供一个参数时,返回一个函数,而不是将第二个参数当作 undefined

将多元函数转化为一元函数才是颗粒化的主要动机,客体化的可替代方案是部分应用和函数绑定。

组合后的函数也是输入和输出之间的引用透明映射

function compose(fn) {
  let args = arguments;
  let start = args.length - 1;
  return function () {
    let i = start;
    let result = args[start].apply(this, arguments);
    while (i--) result = args[i].call(this.result);
    return result;
  };
}

将 compose 增加到 JavaScript 的 function 原型中,还能函数链

// findStudent :: String -> Student
const findStudent = findObject(db);

const csv = ({ ssn, firstname, lastname }) =>
  `${ssn}, ${firstname}, ${lastname}`;

// append :: String -> String -> String
const append = R.curry(function (elementId, info) {
  console.log(info);
  return info;
});

// showStudent :: String -> Integer
const showStudent = R.compose(
  append("#student-info"),
  csv,
  findStudent,
  normalize,
  trim
);

let result = showStudent("44444-4444"); //-> 444-44-4444, Alonzo, Church
assert.equal(result, "444-44-4444, Alonzo, Church");
const runProgram = R.pipe(R.map(R.toLower), R.uniq, R.sortBy(R.identity));

let result = runProgram([
  "Functional",
  "Programming",
  "Curry",
  "Memoization",
  "Partial",
  "Curry",
  "Programming",
]);
assert.deepEqual(result, [
  "curry",
  "functional",
  "memoization",
  "partial",
  "programming",
]);
//-> [curry, functional, memoization, partial, programming]

使用函数组合子来管理程序的控制流

// checkType :: Type -> Type -> Type | TypeError

functor 把值包裹到容器中的模式是为了构建无副作用的代码

引用透明带来的好处是可以进行基于属性的测试

给 function 添加记忆

Function.prototype.memoized = function () {
  let key = JSON.stringify(arguments);
  this._cache = this._cache || {};
  this._cache[key] = this._cache[key] || this.apply(this, arguments);
  return this._cache[key];
};

Function.prototype.memoize = function () {
  let fn = this;
  if (fn.length === 0 || fn.length > 1) {
    return fn;
  }
  return function () {
    return fn.memoized.apply(fn, arguments);
  };
};

持续传递式样(cps) 是一种用于非阻塞程序的编程风格。 回调式函数被称为当前的延续,他们由调用者在返回值上提供。

rxjs 数据作为 Observable 序列

流是随时间发生的有序事件的序列

let res = [];
Rx.Observable.range(1, 3).subscribe(
  (x) => {
    console.log(`Next: ${x}`);
    res.push(x);
  },
  (err) => console.log(`Error: ${err}`),
  () => console.log("Completed")
);
assert.deepEqual(res, [1, 2, 3]);
Rx.Observable.fromEvent(document.querySelector("#student-ssn"), "keyup")
  .pluck("srcElement", "value")
  .map((ssn) => ssn.replace(/^\s*|\s*$|\-/g, ""))
  .filter((ssn) => ssn !== null && ssn.length === 9)
  .subscribe((validSsn) => {
    console.log(`Valid SSN ${validSsn}`);
  });
上一页函数式编程下一页前端input框文字最大值

最后更新于2个月前

这有帮助吗?