【React】《React 学习手册 (第2版) 》笔记-Chapter3-JavaScript 函数式编程

三、JavaScript 函数式编程
  1. 函数可以使用 var、let 或 const 关键字声明,就像声明字符串、数字等变量一样。

    var log = function(message) {console.log(message);
    }const log = message => {console.log(message);
    };
    
  2. 由于函数是变量,那就可以把函数添加到对象中。

    const obj = {message: "xx",log(message) {console.log(message);}
    };
    obj.log(obj.message);
    
  3. 还可以把函数添加到 JavaScript 数组中。

    const message = ["xx",message => console.log(message),"yy",message => console.log(message)
    ];
    message[1](message[0]);
    message[3](message[2]);
    
  4. 函数可以像常规变量一样作为参数发给其他函数。

    const insideFn = logger => {logger("They can be sent to other functions as arguments");
    }insideFn(message => console.log(message));
    
  5. 函数也可以像变量一样由其他函数返回。

    const createScream = function(logger) {return function(message) {logger(message.toUpperCase() + "!!!");};
    };const scream = createScream(message => console.log(message));scream("functions can be returned from other functions");
    scream("createScream returns a function");
    scream("scream invokes that returned function");
    
    • 高阶函数 createScream 还可以用箭头句法表示:
      const createScream = logger => message => {logger(message.toUpperCase() + "!!!");
      };const scream = createScream(message =>console.log(message)
      );scream("ES6 can createScream with less");
      
  6. 函数式编程是一个更大编程范式的一部分,即声明式编程。声明式编程是一种编程风格,采用这种风格开发的应用有个显著特点:重点描述该做什么,而不管怎么做。

  7. React 是声明式的。

  8. 函数式编程的一些核心概念:不可变性、纯函数、数据转换、高阶函数和递归。

  9. 在采用函数式风格的程序中,数据是不可变的,永不更改。我们不直接更改原始数据结构,而是创建数据结构的副本,所有操作都使用副本。

  10. 在 JavaScript 中,函数的参数是对真正数据的引用。

    // 例子1:
    let color_lawn = {title: "lawn",color: "#00FF00",rating: 0
    };function rateColor(color, rating) {color.rating = rating;return color;
    }console.log(rateColor(color_lawn, 5).rating);	// 5
    console.log(color_lawn.rating);					// 5// 例子2:
    let color_lawn = {title: "lawn",color: "#00FF00",rating: 0
    };const rateColor = function(color, rating) {return Object.assign({}, color, { rating: rating });
    };console.log(rateColor(color_lawn, 5).rating);	// 5
    console.log(color_lawn.rating);					// 0// 例子3:
    const rateColor = (color, rating) => ({...color,rating
    });
    
    • 例子2中,我们使用了 Object.assign,它创建了一个空对象,复制颜色对象,然后覆盖副本的评分。
    • 例子3中,使用箭头函数句法和对象展开运算符编写 rateColor 函数。使用展开运算符把颜色对象复制到一个新对象中,然后覆盖评分。
    • 注意例子3中,返回的对象在一对圆括号中。使用箭头函数时一定要这样做,因为箭头不能直接指向一个对象的花括号。
  11. Array.push 不是一个不可变函数。如需不更改原数组,必须使用 Array.concat。Array.concat 的作用是拼接数组。

    // 例子1:
    let list = [{ title: "Rad Red" },{ title: "Lawn" },{ title: "Party Pink" }
    ];var addColor = function(title, colors) {colors.push({ title: title });return colors;
    };console.log(addColor("Glam Green", list).length);	// 4
    console.log(list.length);	// 4// 例子2:
    let list = [{ title: "Rad Red" },{ title: "Lawn" },{ title: "Party Pink" }
    ];const addColor = (title, array) => array.concat({ title });console.log(addColor("Glam Green", list).length);	// 4
    console.log(list.length);	// 3
    
    • 例子2中,concat 方法是接受一个由新颜色的标题构成的对象,把它添加到原数组的副本中。
  12. 与复制对象一样,还可以使用展开运算符拼接数组。

    let list = [{ title: "Rad Red" },{ title: "Lawn" },{ title: "Party Pink" }
    ];const addColor = (title, list) => [...list,{ title }
    ];console.log(addColor("Glam Green", list).length);	// 4
    console.log(list.length);	// 3
    
    • 把原列表复制到一个新数组中,然后把一个包含颜色标题的对象添加到副本中。这就体现了不可变性。
  13. 纯函数指基于参数做计算,并返回一个值的函数。纯函数至少接受一个参数,而且始终返回一个值或另一个函数。这种函数没有副作用、不设置全局变量,也不更改应用的状态。纯函数把参数视为不可变数据。

    var frederick = {name: "Frederick Douglass",canRead: false,canWrite: false
    };const selfEducate = person => ({...person,canRead: true,canWrite: true
    });console.log(selfEducate(frederick));	// {name: 'Frederick Douglass', canRead: true, canWrite: true}
    console.log(frederick);					// {name: 'Frederick Douglass', canRead: false, canWrite: false}
    
    • 这里的 selfEducate 是纯函数。该函数基于传入的值(即 person)做计算,返回一个新 person 对象,而不更改传入的参数,因此没有副作用。
  14. 在 React 中,UI 使用纯函数表达。

  15. 建议遵守下面三条规则编写函数:

    • 函数应该至少接受一个参数。
    • 函数应该返回一个值或另一个函数。
    • 函数不应该更改任何参数。
  16. 在函数式编程中,数据从一种格式变成另一种格式。我们要做的式使用函数产出转换后的副本。这样的函数减少了代码的命令式属性,从而能降低复杂度。若想精通 JavaScript 函数式编程,必须掌握两个核心函数,即 Array.map 和 Array.reduce。

  17. 我们可以使用 Array.join 函数得到一个以逗号分割的清单或其他格式的字符串。Array.join 是 JavaScript 内置的数组方法,作用是从数组中提取出以指定符号分割的字符串。

    const schools = ["Yorktown","Washington & Lee","Wakefield"
    ];console.log(schools.join(", "));	// Yorktown, Washington & Lee, Wakefield
    
  18. 如果想定义一个函数,创建一个新数组,存储名称以”W“开头的学校,可以使用 Array.filter 方法。Array.filter 是 JavaScript 内置的函数,根据源数组产出一个新数组。该函数只接受一个参数,这个参数是一个断言,即一个始终返回布尔值 true 或 false 的函数。Array.filter 在数组的每一个元素上调用断言,元素作为参数传给断言,返回值决定是否把元素添加到新建的数组中,

    const schools = ["Yorktown","Washington & Lee","Wakefield"
    ]const wSchools = schools.filter(school => school[0] === "W")
    console.log( wSchools )		// ['Washington & Lee', 'Wakefield']
    
  19. 若想从数组红删除元素,不建议使用 Array.pop 或 Array.splice,应该使用 Array.filter,因为 Array.filter 执行的是不可变操作。

  20. Array.map 函数接受的参数是一个函数,这个函数是数组的每一个元素上调用一次,不管返回什么都添加到新数组中。

    const schools = ["Yorktown","Washington & Lee","Wakefield"
    ]const highSchools = schools.map(school => ({ name: school }))
    console.log(highSchools)	// [{name: 'Yorktown'}, {name: 'Washington & Lee'}, {name: 'Wakefield'}]
    
    • map 函数可以产出任何 JavaScript 类型的数组,包括对象数组、值数组、嵌套数组和函数数组。
  21. 如果想把数组转换成对象,可以结合 Array.map 和 Object.keys。Object.keys 方法从对象中获取所有键,返回由键构成的数组。

    const schools = {"Yorktown": 10,"Washington & Lee": 2,"Wakefield": 5
    }const schoolArray = Object.keys(schools).map(key => ({name: key,wins: schools[key]
    }))console.log(schoolArray)
    
  22. reduce 和 reduceRight 函数可用于把数组转换成任何值,包括数组、字符串、布尔值、对象,甚至是函数。

    const ages = [21,18,42,40,64,63,34]const maxAge = ages.reduce((max, age) => {console.log(`${age} > ${max} = ${age > max}`)if (age > max) {return age} else {return max}
    }, 0)console.log('maxAge', maxAge)21 > 0 = true
    18 > 21 = false
    42 > 21 = true
    40 > 42 = false
    64 > 42 = true
    63 > 64 = false
    34 > 64 = false
    maxAge 64// 可简化为:
    const max = ages.reduce((max, value) => (value > max) ? value : max, 0)
    
    • ages 数组被归约为一个值了,即最大年龄64。
    • reduce 函数接受两个参数:一个回调函数和一个初始值。在这个示例中,初始值是0,即先把最大值设为0。回调函数在数组中的每个元素上调用一次。首次调用回调函数时,age等于21,即数组中的第一个值,max等于0,即设置的初始值。回调函数返回的这两个数中较大的那一个,即21,下次迭代将把这个值赋为max。每次迭代比较各个age值与max值,返回二者中较大的那个。最后,比较数组中最后一个数和前一次调用回调函数返回的数。
  23. Array.reduceRight 的作用与 Array.reduce 相同,只不过不从数组开头开始归约,而是从数组末尾开始。

  24. 把数组转换成对象。

    const colors = [{id: '-xekare',title: "rad red",rating: 3},{id: '-jbwsof',title: "big blue",rating: 2},{id: '-prigbj',title: "grizzly grey",rating: 5},{id: '-ryhbhsl',title: "banana",rating: 1}
    ]const hashColors = colors.reduce((hash, {id, title, rating}) => {hash[id] = {title, rating}return hash
    }, {})console.log(hashColors)
    
    • 在这个示例中,传给 reduce 函数的第二个参数是一个空对象。这是创建散列的初始值。每次迭代,回调函数使用方括号句法为散列添加一个键,把键的值设为数组中 id 字段的值。
  25. 使用 reduce 甚至可以把数组转换成完全不同的数组。

    const colors = ["red", "red", "green", "blue", "green"];const uniqueColors = colors.reduce((unique, color) =>(unique.indexOf(color) !== -1) ? unique : [...unique, color],[]
    )console.log(distinctColors)
    
    • 这个示例把 colors 数组归约为一个没有重复值的数组。
  26. 高阶函数指用于处理其他函数的函数,其参数可以是函数,也可以返回函数,或者二者兼而有之。

  27. 第一类高阶函数是接受其他函数为参数的函数。Array.map、Array.filter 和 Array.reduce 都接受函数作为参数,因此它们是高阶函数。

    const invokeIf = (condition, fnTrue, fnFalse) => (condition) ? fnTrue() : fnFalse()const showWelcome = () => console.log("Welcome!!!")const showUnauthorized = () => console.log("Unauthorized!!!")invokeIf(true, showWelcome, showUnauthorized)	// Welcome!!!
    invokeIf(false, showWelcome, showUnauthorized)	// Unauthorized!!!
    
  28. 返回一个函数的高阶函数有助于我们处理 JavaScript 复杂的异步操作,创建我们在必要时需要使用或重用的函数。

    const getFakeMembers = count => new Promise((resolves, rejects) => {const api = `https://api.randomuser.me/?nat=US&results=${count}`const request = new XMLHttpRequest()request.open('GET', api)request.onload = () =>(request.status === 200) ?resolves(JSON.parse(request.response).results) :reject(Error(request.statusText))request.onerror = (err) => rejects(err)request.send()
    })const userLogs = userName => message =>console.log(`${userName} -> ${message}`)const log = userLogs("grandpa23")log("attempted to load 20 fake members")
    getFakeMembers(20).then(members => log(`successfully loaded ${members.length} members`),error => log("encountered an error loading members")
    )grandpa23 -> attempted to load 20 fake members
    grandpa23 -> successfully loaded 20 members
    或
    grandpa23 -> attempted to load 20 fake members
    grandpa23 -> encountered an error loading members
    
    • userLogs 函数保存一些信息,返回一个函数,以便在其他信息可用时使用或重用。
    • userLogs 是高阶函数。log 函数由 userLogs 得来,每次调用 log 函数都会在消息前面加上“grandpa23”。
  29. 递归技术指创建重新调用自身的函数。如果要解决的问题涉及循环,往往就可以转而使用递归函数。

    const countdown = (value, fn) => {fn(value)return (value > 0) ? countdown(value-1, fn) : value
    }countdown(10, value => console.log(value))
    
    • countdown 是递归函数。
  30. 递归模式特别适合在异步操作中使用。函数在做好准备时调用本身,例如在数据可用时,或者当计时器结束时。

    const countdown = (value, fn, delay=1000) => {fn(value)return (value > 0) ?setTimeout(() => countdown(value-1, fn), delay) : value
    }const log = value => console.log(value)
    countdown(10, log)
    
    • 修改后的版本可用于创建倒计时钟表。
  31. 递归技术适合在搜索数据结构中使用。可以递归迭代子文件夹,直到找出仅包含文件的文件夹为止。也可以递归迭代 HTML DOM,直到找到一个没有子节点的元素为止。

    const dan = {type: "person",data: {gender: "male",info: {id: 22,fullname: {first: "Dan",last: "Deacon"}}}}const deepPick = (fields, object={}) => {const [first, ...remaining] = fields.split(".")return (remaining.length) ? deepPick(remaining.join("."), object[first]): object[first]
    }console.log( deepPick("type", dan) )    // person
    console.log( deepPick("data.info.fullname.first", dan) )    // Dan
    
    • 通过数组析构把第一个值与其余的值分开。
  32. 函数式程序是把逻辑拆分成一系列关注特定任务的小型纯函数。最终,你要把这些小型函数整合到一起。合成的方式、模式和技术有很多种,你可能熟悉的一种是串接。在 JavaScript 中,函数可以使用点号记法串接在一起,依次处理前一个函数的返回值。

  33. 字符串有 replace 方法。replace 方法返回一个模块字符串,而模板字符串也有 replace 方法。因此,可以使用点号记法把 replace 方法串接在一起处理字符串。

    const template = "hh:mm:ss tt"
    const clockTime = template.replace("hh", "03").replace("mm", "33").replace("ss", "33").replace("tt", "PM")console.log(clockTime)  // 03:33:33 PM
    console.log(template)   // hh:mm:ss tt
    
    • template 自身完好无损,可以继续用于创建要显示的其他时间。
  34. 当一个函数充当了在两个函数之间传递数据的管道,但像下面的例子,这样的句法难以理解,也不易维护和扩充。

    const createClockTime = date => ({date})const appendAMPM = ({date}) =>({date,ampm: (date.getHours() >= 12) ? "PM" : "AM"})const civilianHours = clockTime => {const hours = clockTime.date.getHours()return {...clockTime,hours: (hours > 12) ?hours - 12 :hours}
    }const removeDate = clockTime => {let newTime = {...clockTime}delete newTime.datereturn newTime
    }// Not the best way to compose all of these into one functionconst oneFunction = date =>removeDate(civilianHours(appendAMPM(createClockTime(date))))console.log(oneFunction(new Date()))
    
  35. 一种更为优雅的方法是创建一个高阶函数,把多个函数合成为一个大型函数。

    const createClockTime = date => ({date})const appendAMPM = ({date}) =>({date,ampm: (date.getHours() >= 12) ? "PM" : "AM"})const civilianHours = clockTime => {
    const hours = clockTime.date.getHours()return {...clockTime,hours: (hours > 12) ?hours - 12 :hours}
    }const removeDate = clockTime => {let newTime = {...clockTime}delete newTime.datereturn newTime
    }// A more elegant approach to compositionconst compose = (...fns) =>(arg) =>fns.reduce((composed, f) => f(composed),arg
    )const oneFunction = compose(createClockTime,appendAMPM,civilianHours,removeDate
    )console.log(oneFunction(new Date()))   // { "ampm": "PM", "hours": 8 }
    
    • 这样的写法易于扩充,随时都可以添加更多函数。
    • 另外,这种方法也方便修改组合函数的顺序。
    • compose 函数是一个高阶函数,接受的参数是函数,只返回一个值。
    • compose 接受的参数是函数,返回一个函数。在上述实现中,我们使用展开运算符把通过参数传入的函数转换成一个名为 fns 的数组。返回的函数接受一个参数,即 arg。调用这个函数时,首先填充 fns 数组的是想通过该函数发送的参数。这个参数作为 compose 的初始值,随后各值是每次迭代 reduce 的回调返回的值。
    • 注意,回调函数接受两个参数:composed 和一个函数 f。调用每个函数时传入的参数是 composed,即前一个函数的输出结果。最终,将调用最后一个函数,返回最后的结果。
  36. 在函数式程序中,应该尽量使用函数,而非值。需要时,我们可以调用函数来获取值。

    const oneSecond = () => 1000
    const getCurrentTime = () => new Date()
    const clear = () => console.clear()
    const log = message => console.log(message)
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/28066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android OTA升级之后,apk崩溃无法启动

硬件平台:QCS6125 软件平台:Android 11 问题背景:系统版本从低版本升级到高版本后,apk崩溃启动失败。启动失败的activity为apk新增加的组件,报错的信息为: ActivityNotFoundException: Unable to find ex…

[leetcode]将二叉搜索树转化为排序的双向链表

. - 力扣(LeetCode) /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node() {}Node(int _val) {val _val;left NULL;right NULL;}Node(int _val, Node* _left, Node* _right) {val _val;left _left;right _rig…

目标检测数据集 - 零售食品LOGO检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍:零售食品 LOGO 检测数据集,真实零售食品 LOGO 高质量商品图片数据,数据集含常见零售食品 LOGO 图片,包括饮料类、酒类、调味品类、膨化饼干类、巧克力类、常见零食类等等。数据集类别丰富,标注标签包含 150…

react中组件的生命周期

React组件的生命周期是指组件从被创建、挂载到页面,到组件更新,再到组件被销毁的整个过程。在这个过程中,React提供了一系列的钩子函数(生命周期方法),允许开发者在组件的不同阶段执行特定的操作。以下是Re…

Java中如何调用mysql中函数

在Java中调用MySQL中的函数(无论是存储函数还是自定义函数),通常是通过JDBC(Java Database Connectivity)来完成的。以下是一个简单的步骤说明和示例代码,展示如何在Java中调用MySQL中的函数。 步骤 添加…

简单的线程池示例

线程池可以有效地管理和重用线程资源&#xff0c;避免频繁创建和销毁线程带来的开销。以下是一个简单的线程池示例。 cpp #include <iostream> #include <vector> #include <thread> #include <queue> #include <mutex> #include <condition…

Python闯LeetCode--第3题:无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 思路解题方法复杂度Code 思路 一上来马上想到两层for循环暴力枚举&#xff0c;但是又立马想到复杂度是 O ( n 2 ) O(n^2) O(n2)&#xff0c;思考了一下能否有更优解&#xff0c;于是想到用头尾两个指针来指定滑动窗口&#xff08;主…

DeepSORT(目标跟踪算法)中卡尔曼增益的理解

DeepSORT&#xff08;目标跟踪算法&#xff09;中卡尔曼增益的理解 flyfish 先用最简单的例子来理解卡尔曼增益 公式 (1) 首先&#xff0c;通过多次测量一个物理量&#xff0c;并使用取平均值的方式来计算其真实值&#xff1a; x ^ k 1 k ( z 1 z 2 ⋯ z k ) \hat{x}_…

python-基础篇-函数-在py中的长相

文章目录 整体长相长相要求 整体长相 怎么自定义函数&#xff1f; 要知道怎么定义函数&#xff0c;就要知道函数的组成部分是怎样的。 def 函数名(参数1&#xff0c;参数2....参数n):函数体return 语句这就是 Python 函数的组成部分。 长相要求 所以自定义函数&#xff0c…

父亲节:我要做爸爸的健康监督员

父亲节将至&#xff0c;总想着能为爸爸做些什么&#xff0c;来表达我们的感激与关爱。在这个特殊的日子里&#xff0c;成为爸爸的健康监督员&#xff0c;用华为 Watch 4 的智慧健康功能&#xff0c;任何时刻都可以关注爸爸的健康状况&#xff0c;放心又安心了。 用一键微体检…

使用 python 将 Markdown 文件转换为 ppt演示文稿

在这篇博客中&#xff0c;我们将展示如何使用 wxPython 创建一个简单的图形用户界面 (GUI)&#xff0c;以将 Markdown 文件转换为 PowerPoint 演示文稿。我们将利用 markdown2 模块将 Markdown 转换为 HTML&#xff0c;并使用 python-pptx 模块将 HTML 内容转换为 PowerPoint 幻…

[Vulnhub]Wintermute LFI+SMTP+Screen+Structv2-RCE+Lxc逃逸

概要 靶机 192.168.8.104 信息收集 $ nmap 192.168.8.103 --min-rate 1000 -sC -sV 结果: Starting Nmap 7.92 ( https://nmap.org ) at 2024-06-15 05:54 EDT Nmap scan report for 192.168.8.103 (192.168.8.103) Host is up (0.035s latency). Not shown: 997 closed t…

亚马逊测评自养号与机刷的区别

前言&#xff1a; 在亚马逊运营的领域中&#xff0c;经常有人问&#xff1a;测评自养号就是机刷吗&#xff1f;它们两者有什么区别&#xff1f;做自养号太慢、太需要时间了&#xff0c;如果用机刷的话&#xff0c;会不会简单高效一点&#xff1f; 在这篇文章中&#xff0c;我…

Java装饰者模式详解:为对象动态添加功能

装饰者模式是一种允许向单个对象添加新功能的设计模式&#xff0c;而不是向整个类添加特性。这种模式创建了一个包装对象&#xff0c;也称为“装饰者”&#xff0c;这个包装对象包含了主对象的引用以及新增的功能。本文将探讨装饰者模式的结构、实现方式以及在Java中的应用示例…

SpringCloud 网关Gateway配置并使用

目录 1 什么是网关&#xff1f; 2 Gateway的使用 2.1 在其pom文件中引入依赖 2.2 然后gateway配置文件中配置信息 2.3 启动网关微服务 3 网关处理流程 4 前端-网关-微服务-微服务间实现信息共享传递 1 什么是网关&#xff1f; 网关&#xff1a;就是网络的关口&#xff…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 游戏表演赛分队(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 游戏表演赛分队(100分) 🌍 评测功能需要订阅专栏后私信联系…

【Android面试八股文】 GC的流程是怎么样的?介绍下GC回收机制与分代回收策略

文章目录 一、什么是垃圾(Garbage)?二、什么是可行性分析?三、什么是GC Root对象?四、引用关系有哪些?五、什么时候进行垃圾回收?六、垃圾收集算法6.1 标记-清除(Mark-Sweep)6.2 复制(Copying)6.3 标记-整理/压缩(Mark-Compact)6.4 分代收集(Generational Garbag…

No module named ‘torch.distributed.checkpoint.format_utils‘问题解决

完整代码&#xff1a; Traceback (most recent call last):File "/data/user/BMLU-use/src/English_chat/qwen1.5.py", line 97, in <module>main(model_pathargs.model_path,max_lengthargs.max_length,nameargs.name)File "/data/user/BMLU-use/src/En…

Python | Leetcode Python题解之第148题排序链表

题目&#xff1a; 题解&#xff1a; class Solution:def sortList(self, head: ListNode) -> ListNode:def merge(head1: ListNode, head2: ListNode) -> ListNode:dummyHead ListNode(0)temp, temp1, temp2 dummyHead, head1, head2while temp1 and temp2:if temp1.v…

2024hw蓝队面试题-1

使用过哪些设备&#xff0c;出现误报怎么办&#xff1f; 我使用过的设备包括各种防火墙、入侵检测系统(IDS)、入侵防御系统(IPS)、安全信息和事件管理(SIEM)系统等。在遇到误报时&#xff0c;我通常会首先确认这是一个真正的误报&#xff0c;而不是对系统的实际威胁。确认后&a…