Logo
Section 1 / 5

与对象一起使用

焦距聚焦于数据结构的一部分。它们具有可组合性,能够高效地读取或更新被聚焦的部分。(5分钟阅读)

导入

与对象一起使用

焦距聚焦于数据结构的一部分。它们具有可组合性,能够高效地读取或更新被聚焦的部分。(5分钟阅读)

希望前面的几个练习对你构成了挑战。现在,让我们放松一下,深入学习一些理论知识。

透镜(Lenses)

正如其名,透镜让你能够“聚焦”于数据结构中的某个特定部分。

获取器(Getters)

我们以老朋友 Bobo 为例。

1import { lensProp, view } from 'ramda'; 2 3const person = { 4 firstName: 'Bobo', 5 lastName: 'Flakes' 6}; 7 8const fNameLens = lensProp('firstName'); 9const result = view(fNameLens, person); 10 11console.log({ result });

lensProp 创建一个聚焦于对象属性的透镜。在这个例子中,fNameLens 会查找任意对象的 firstName 属性。将其与 person 一起传给 view,即可获取 Bobo 的名字。

设置器(Setters)

更改 Bobo 的名字同样简单。只需使用 set 并传入目标值即可。

1import { lensProp, set } from 'ramda'; 2 3const person = { 4 firstName: 'Bobo', 5 lastName: 'Flakes' 6}; 7 8const fNameLens = lensProp('firstName'); 9const result = set(fNameLens, 'Bobo Jr.', person); 10 11console.log({ person }); 12console.log({ result });

如果你希望通过函数来修改,可以使用 over。

1import { concat, lensProp, over } from 'ramda'; 2 3const person = { 4 firstName: 'Bobo', 5 lastName: 'Flakes' 6}; 7 8const fNameLens = lensProp('firstName'); 9const result = over(fNameLens, concat('Mr. '), person); 10 11console.log({ person }); 12console.log({ result });

注意,set 和 over 并没有改变原始的 person 对象。Ramda 函数不会修改输入数据,而是返回一个新的结果。

嵌套属性

透镜非常适合安全地修改嵌套属性,而无需编写大量合并代码。

如果用原生 JavaScript 来实现,如何不可变地将 Bobo 的经理姓氏改为 "Flakes"?大概会是这样:

1const person = { 2 firstName: 'Bobo', 3 lastName: 'Flakes', 4 company: 'Fake Inc.', 5 position: { 6 title: 'Front-End Developer', 7 department: { 8 name: 'Product', 9 departmentManager: { 10 firstName: 'Bobo Sr.', 11 lastName: 'Flax' 12 } 13 } 14 } 15}; 16 17const correctPerson = { 18 ...person, 19 position: { 20 ...person.position, 21 department: { 22 ...person.position.department, 23 departmentManager: { 24 ...person.position.department.departmentManager, 25 lastName: 'Flakes' 26 } 27 } 28 } 29}; 30 31const correctedLastName = correctPerson.position.department.departmentManager.lastName; 32 33console.log({ correctedLastName });

即使使用了 ES6 的展开语法,代码仍然不够简洁。我们来试试透镜的方式。

1import { lensPath, set, view } from 'ramda'; 2 3const person = { 4 firstName: 'Bobo', 5 lastName: 'Flakes', 6 company: 'Fake Inc.', 7 position: { 8 title: 'Front-End Developer', 9 department: { 10 name: 'Product', 11 departmentManager: { 12 firstName: 'Bobo Sr.', 13 lastName: 'Flax' 14 } 15 } 16 } 17}; 18 19const managerLastNameLens = lensPath([ 20 'position', 21 'department', 22 'departmentManager', 23 'lastName' 24]); 25 26const correctPerson = set(managerLastNameLens, 'Flakes', person); 27const correctedLastName = view(managerLastNameLens, correctPerson); 28 29console.log({ correctedLastName });

清晰得多。还有一个额外的好处:由于透镜是柯里化的,同一个透镜可以用于查看和更新属性。我们在这里同时在 set 和 view 中使用了 managerLastNameLens。

函子

与数组一起使用