JavaScript 进阶实战课
石川
JavaScript Patterns and Anti-Patterns 等开源项目创建者,O'Reilly 技术评审
15066 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
开篇词 (1讲)
JavaScript 进阶实战课
15
15
1.0x
00:00/00:00
登录|注册

02 | 如何通过闭包对象管理程序中状态的变化?

你好,我是石川。
通过上节课的学习,现在我们知道,函数式编程中存在副作用(side effect),而纯函数和不可变就是减少副作用的两个核心思想。那么按理说,我们要想把副作用降低到接近为零,就可以用纯函数,同时不接受任何参数。但是这样完全自我封闭的函数,也就几乎没有什么使用意义了。
所以,作为一个函数,还是要有输入、计算和输出,才能和外界有互动往来,我们的系统也才能“活”起来。而一个活的系统,它的状态肯定是在不停变化的,那么我们如何才能在不可变的原则下,来管理这种变化呢?
今天这节课,我们就一起来看看在函数式编程中,有哪些值是可变的、哪些不可变,以及如何能在状态更新的同时做到不可变。

值的(不)可变

首先,我们要搞清楚一个问题,值到底是不是可变的?在 JavaScript 中,值一般被分为两种:原始类型和对象类型。
先来看原始类型。像字符串或数字这种数据类型,都是属于原始类型,而它们本身是不可变的。举个例子:在 console.log 中输入 2 = 2.5,得到的结果会是 invalid,这就证明了我们不可能改变一个原始类型的值。
2 = 2.5 // invalid
然后是对象类型。在 JavaScript 中,像数组、对象这类数据类型就叫做对象类型,这类数据更像是一种数据结构或容器。那这样的“值”是否可变?其实通过上节课数组的例子,你能看到这类值是可变的,比如通过 splice 这种方法。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了在函数式编程中如何通过闭包对象管理程序状态的变化。首先介绍了函数式编程中的副作用和纯函数的概念,以及在不可变原则下管理状态变化的重要性。随后分析了JavaScript中的原始类型和对象类型的可变性,以及React.js中的props和state的使用。重点讨论了闭包和对象在封装状态和创建行为方面的作用,并对它们在隐私、状态拷贝和性能等方面的差异进行了比较。文章还探讨了属性的查改操作对闭包和对象的影响,并介绍了通过Object.freeze()方法实现对象属性的只读设置。总结指出,在React.js中,对象作为props和state的值类型更容易保证属性和状态值的整体不可变,更易于拷贝,并在处理高频交互时性能更佳。而闭包虽有隐私上的优势和更细粒度的操作,但在应用交互和状态管理方面并没有实际作用。最后强调了根据具体情况确定哪种方式更适合程序和应用所需支持的场景。整体而言,本文通过具体的例子和分析,为读者提供了深入的技术理解和应用指导。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《JavaScript 进阶实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(15)

  • 最新
  • 精选
  • WGH丶
    有一个说法是:闭包是带数据的行为,对象是带行为的数据。

    作者回复: 很好地描述了两者相对的从属关系

    2022-09-28归属地:北京
    39
  • I keep my ideals💤
    如果要实现值的绝对不可变应该使用深拷贝,这样对拷贝出来的复杂数据结构进行修改时才能保证不会对原始数据造成影响

    作者回复: 是的

    2022-09-22归属地:北京
    7
  • 谷岳
    spread 展开语法仅能做到浅拷贝,因为仅遍历一层。在开发中,比较常用的深拷贝方式是:JSON.parse(JSON.stringify(obj))。虽然stringify方法在转化JSON字符串时有不少特殊状况。这种方式不会影响状态,因为stringify方法返回的是一个常量字符串。

    作者回复: 是的,虽然React.PureComponent中的shouldComponentUpdate() 基于性能考虑,不建议用深对比和JSON.stringify();但如果程序中确实是需要处理复杂的数据结构变化的话,可以用force update或immutable-js来满足类似的需求。

    2022-09-22归属地:北京
    5
  • 雨中送陈萍萍
    好像要老师画图的软件,老师可以告知一下麽

    作者回复: Mac自带的keynote,没有特别的工具哈,类似于Windows中的ppt。

    2022-11-03归属地:北京
    2
  • Alison
    通常更新state的时候框架会用Object.is来判断2个数组/对象是否相等,浅拷贝对象时,因拷贝的是引用地址,所以Object.is对比后的返回值会是true,状态就无法正常更新; 深拷贝对象的话,拷贝的是值,此时会产生新的引用地址,所以Object.is对比后的返回值是false,状态会进行更新

    作者回复: 在React,早期有一个shallowCompare附加功能,后面被React.memo和React.PureComponent取代了,但是底层逻辑类似,仍然是一个浅对比。如果想对比更复杂的对象,React.memo也支持在第二个实参传入自定义的对比功能。

    2022-09-22归属地:北京
    2
  • Hello,Tomrrow
    对象或数组的浅拷贝,是简单的值的复制,这对于对象属性值或数组元素是简单类型来说没有问题;如果对象属性值或数组元素是复杂类型,存的是一个内存地址,对内存地址的复制,只是多了一个指向同一个空间的指针。这时需要进行深拷贝,常用的方式通过递归的方式。 深拷贝,是不会影响状态管理的。

    作者回复: 深拷贝的简单实现是JSON.parse(JSON.stringify(obj)),不过考虑到JSON safe和性能问题,递归是会更好一些。 React中的setState()是浅合并而不是深拷贝,会不会影响看情况,如果是“复杂类型”也就是嵌套对象,那就会被影响了。

    2022-09-22归属地:北京
    2
  • Change
    文中提到闭包比较难实现拷贝,比较有疑问? 1. 闭包如果返回属性则失去对属性保护的意义。 2. 如果不返回通过哪种方式是实现属性拷贝。

    作者回复: 1. 闭包如果返回属性则失去对属性保护的意义。 --------------------------------- 是的,所以对象的属性是公开的,闭包的意义就是隐藏。 2. 如果不返回通过哪种方式是实现属性拷贝。 --------------------------------- 这种情况下,就要在闭包嵌套层加获取权限的方法。

    2022-10-11归属地:北京
    1
  • 奕晨
    spread 做到的是浅拷贝,那么你是否了解与之对应的深度拷贝? 需要根据数据类型区分,若是对象的话,浅拷贝后,修改其中一个值,会影响另一个值,拷贝的是对象的地址;深度拷贝就是重新创建一个新的地址,修改其中的值,互不影响。 它会不会影响状态的管理? 需要针对props 和state区分,根据值的类型判断,props不会,state会影响。

    作者回复: 对于嵌套的对象,是有这样的影响。

    2022-09-22归属地:北京
    1
  • 比如下面的 [3, 1, 0, 7] 这组数组中,我们把第一个值变成 2,第三个值变成 6,第 4 个值添加 1,形成了 [2, 1, 0, 6, 1]。 老师,这里没太理解呢,第3个值变成6,不是 2,1,6 吗?

    作者回复: 可能是描述的问题,我再改改。因为是数组,这里我们从0开始计算第一位,然后第3个值变成6是第四位,再然后第4个值添加1是第五位。

    2022-11-01归属地:北京
    2
  • 哎呦先生
    老师,扩展运算符例子那数组元素用引用类型的数据结构是不是比较合理,原始类型的数据无法深拷贝浅拷贝的内存地址区别,容易迷糊。

    作者回复: 这里咱们用的数组和对象就是引用类型的数据结构哈。 举个例子: var a = [1,2,3]; var b = a; a[3] = 4; console.log(b); //返回 [1,2,3,4] 你可以看到b引用的是a的数组中的元素。

    2022-10-31归属地:北京
收起评论
显示
设置
留言
15
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部