24 Matching Annotations
  1. May 2024
    1. => 箭头函数的匿名性是 => 的阿喀琉斯之踵。这让我不能遵守刚刚所说的命名原则了:阅读困难,调试困难,无法自我引用。

      的确如此

    2. 顺便说一句,匿名的意思是什么?具体来说,函数具有一个 name 的属性,用于保存函数在语法上设定名称的字符串值,例如 "helloMyNameIs" 或 "FunctionExpr"。 这个name 属性特别用于 JS 环境的控制台或开发工具。当我们在堆栈轨迹中追踪(通常来自异常)时,这个属性可以列出该函数。

      匿名函数很令人头疼,同时React的匿名组件也有类似的问题。

      因此推荐使用 const foo = ()=>{} 的形式来写函数。借助ES6能够从名称引用中给匿名函数推导出名称

      但是如果foo未被定义,则容易出问题

    3. 我们将在本书的后续中大量使用闭包。如果抛开整个编程来说,它可能是所有函数式编程中最重要的基础。希望你能用得舒服!

      是的,闭包关联了多个重要的概念。作用域、垃圾收集、以及多种设计模式的实现。

    4. 在所有编程,尤其是函数式编程中,最强大的就是:当一个函数内部存在另一个函数的作用域时,对当前函数进行操作。当内部函数从外部函数引用变量,这被称作闭包。

      一个函数内部嵌套了另外一个函数,此时内部函数体中可以访问外层函数的作用域,这就是闭包。

      闭包的用途,可以用于实现各种设计模式,是JS的核心概念之一

    5. 将其他函数视为值的函数是高阶函数的定义。函数式编程者们应该学会这样写!

      函数是一等公民的意思是,函数跟其他数据类型一样,在传值时被同等对待,也就是能够被传递进函数并从函数返回

    6. 这个隐式函数输出在函数式编程中有一个特殊的名称:副作用。当然,没有副作用的函数也有一个特殊的名称:纯函数。我们将在以后的章节讨论这些,但关键是我们应该喜欢纯函数,并且要尽可能地避免副作用。

      例如可以将副作用集中、转化,使函数尽可能纯粹而不具有副作用

    7. 但是,改变一个外部作用域的变量,就像我们在 foo(..) 中所做的赋值 y 一样,只是实现隐式输出的一种方式。一个更微妙的例子是通过引用对非局部值进行更改。

      尽量避免使用隐式输出,也就是不要在函数体中修改外部变量,也不要修改函数形参的值。

      显示return是更佳的选择。

    8. 我不是说,你只能有一个 return,或你不应该提早 return,我只是认为在定义函数时,最好不要用 return 来实现流控制,这样会创造更多的隐含意义。尝试找出最明确的表达逻辑的方式,这往往是最好的办法。

      一个可以尽早退出的例子是判断函数是否需要继续执行下去,作为一种守护代码而存在。

      文章中的例子更像是用来得到一个新的值,此时需要谨慎使用return来实现流程控制,更推荐使用if来控制执行流程,并最终return出结果

    9. 提示: 在这里我十分建议你花一点时间来思考:是否需要避免函数有可重构的多个输出?或许将这个函数分为两个或更多个更小的单用途函数。有时会需要这么做,有时可能不需要,但你应该至少考虑一下。

      的确需要考虑,衡量的标准应该是函数的纯粹性:

      这个函数的功能是否是单一的,是否可以接着拆解成更细粒度的函数。

    10. function foo() { var retValue1 = 11; var retValue2 = 31; return [ retValue1, retValue2 ]; } var [ x, y ] = foo(); console.log( x + y ); // 42

      在编写React Hooks时会很自然用到这种写法:

      js const [xxx, fetchXXX] = useXxx()

    11. 如果你没有 return 值,或者你使用 return;,那么则会隐式地返回 undefined 值。 如果想要尽可能靠近函数式编程的定义:使用函数而非程序,那么我们的函数必须永远有返回值。这也意味着他们必须明确地 return 一个值,通常这个值也不是 undefined。

      这也是React会在render函数中的if语句中报错的原因,隐式返回undefined

    12. 通过不同的输入值让一个函数重载拥有不同的行为的技巧叫做特定多态(ad hoc polymorphism)。

      重载,多态

    13. 程序员这样定义函数的原因之一是,更容易通过同一个函数来重载不同的功能。最广为人知的例子就是 jQuery 提供的 $(..)。"$" 函数大约有十几种不同的功能 —— 从 DOM 元素查找,到 DOM 元素创建,到等待 “DOMContentLoaded” 事件后,执行一个函数,这些都取决于你传递给它的参数。

      是根据不同的实参来执行不同的函数【逻辑】

    14. 回想一下,术语 Arity 是指期望函数接收多少个参数。Arity 为 1 的函数也被称为一元函数。在函数式编程中,我们希望我们的函数在任何的情况下是一元的,有时我们甚至会使用各种技巧来将高 Arity 的函数都转换为一元的形式。

      这意味着函数的形参是一个,但是可以通过解构来从一个形参中读取多个参数。这简化了参数的传递,从而降低理解成本

    15. 只要可能,无论我们的语言和我们的库或框架允许我们达到什么程度,我们都应该尽可能使用声明性的和自解释的代码。

      声明式的就是说,我期望有xxx,数量是xxx,是对一种期望的状态的描述,但是如何达到这一状态则不再描述。

      命令式则逐行描述需要执行的具体操作,在操作最终执行完之前,无法得知最终的结果

    16. 数组解构

      有点像在讲ES6的小技巧,但是确实比ES5要好用得多,可以代替很多工具函数

    17. 无论采取什么行为, ... 都会让实参数组更容易操作。那些我们使用实参数组 slice(..),concat(..) 和 apply(..) 的日子已经过去了。

      也就是有了... 就不需要使用 arguments 来操作实参数组了

    18. 牺牲向后的兼容性

      想象是一个一直向右的箭头代表滚滚向前的时间,有个小人面向箭头前进的方向。

      此时向后兼容意味着从小人的角色来看,是在兼容在过去编写的代码,也就是所谓的历史代码。因此向后兼容可以简单记录为兼容老代码。

      而向前兼容就可以说是老代码可以在新的平台、框架上运行。

    19. foo(..) 期望三个实参,因为它声明了三个形参。这里有一个特殊的术语:Arity。Arity 指的是一个函数声明的形参数量。 foo(..) 的 Arity 是 3。

      foo.length可以获取函数形参的数量。实参是实际传入函数的值,而形参是函数参数的形状,也就是用于接收传入值的明明变量

    1. 最好的代码是可读性高的代码,因为它在正确的(理想主义)和必然的(正确的)之间寻求到了恰到好处的平衡。

      在极端简单的代码和极端臃肿混乱的代码之间,折中选择了一种可读性更好的代码。

      当然,最好的代码是没有代码

    2. 我们将在下一章更深入的讨论这个问题。但是你可能写过一些命令式的代码,像 if 语句和 for 循环这样的语句。这些语句旨在精确地指导计算机如何完成一件事情。声明式代码,以及我们努力遵循函数式编程原则所写出的代码,更专注于描述最终的结果。

      声明式写法和命令式写法的挑战无处不在。例如各种运维系统中往往崇尚声明式写法,但是也有人对aws的yml文件深恶痛绝。

    3. 你知道研究过这个话题的专家给出了怎样的数据吗?我们在维护代码过程中 70% 的时间花在了阅读和理解代码上。 也难怪全球程序员每天的平均代码行数是 5 行。我们一天花七个半小时用来读代码,然后找出这 5 行代码应该写在哪里。

      工作时间越长,越能理解这一点。

    4. 根据以往经验你可能知道,有时候花很多时间“编程”其实只是读现有的代码。我们的大部分时间其实都是在维护别人的代码(或自己的老代码),只有少部分时间是在敲新代码。

      的确如此,就算是从零开始一个全新的想项目,随着项目的逐步发展,阅读先有代码的时间也会超过编写新代码的时间

    5. 信任是什么意思?信任是指你通过读代码,不仅是跑代码,就能理解这段代码能干什么事,而不只是停留在它可能是干什么的层面。

      代码的第一要义是供人阅读,例如人们可以直接阅读仓库中的源码文件来搞清楚这块代码的逻辑,有时候甚至不借助具备丰富功能的IDE就能做到这一点。