Edge
  • Introduction
  • 文章
    • 如何将 emoji 当成单色 icon 使用
    • 在 web 使用 JavaScript 模块
    • 从零开始写一个 Promise 库
    • 魔幻语言 JavaScript 系列之类型转换、宽松相等以及原始值
    • React 整洁代码最佳实践
    • 魔幻语言 JavaScript 系列之 call、bind 以及上下文
    • 编写扁平化的代码
    • ES6 Promise:模式与反模式
    • 在 Node.js 中使用原生 ES 模块
    • 在工作中学习
    • JavaScript 中的匿名递归
    • 面向初学者的高阶组件介绍
    • CSS Animations vs Web Animations API
    • JavaScript 异常的防范与监控
    • 喜欢用 Git 做的一些小事
    • 移除在 ESM 模式生成的严格模式
    • 编写现代 JavaScript 代码
    • JavaScript 模块化现状
    • JS/React 开发者的 Atom 终极配置
    • 使用 ES6 的浏览器兼容性问题
    • 为什么是 JSONP
    • 实现不定尺寸图片居中的几种方式
    • Base64 简要笔记及其他
    • 关于内存对齐的一点注解
    • 康威生命游戏的简单实现
    • 使用双端队列模拟动态数组的部分功能
    • 三种变长编码方法的 C 实现
  • 聚沙成塔
    • Node.js
      • 包管理器
    • 基于 OS X 开发指南
    • OS X 小技巧
    • 基于 Windows 开发指南
    • Web Tools
    • Service Worker
    • vim
    • shell
    • 奇技
    • 程序员
    • BFC
    • 事件循环
    • 获取自定义全局变量
    • 颜色格式转换 rgb -> hex
    • 页面间 post 传参
    • 函数重载
    • Tree shaking
    • RequireJS tips
  • 算法
    • 约瑟夫问题
    • 简单排序
    • 快速排序
    • 快速排序优化
    • 三路快排和选择
    • 裴波那契
  • ECMAScript
    • 原型
    • Object.is
  • ES6+
    • ES6 Modules
    • import & export
  • React
    • setState
    • react 与 iscroll 的点击问题
    • pureRender
  • git
    • 重写提交信息
    • http 记住密码
  • 拾遗
    • 拾遗
Powered by GitBook
On this page
  • Unwind
  • 闭包
  • 再深入一点
  • 匿名递归
  • 结论
  1. 文章

JavaScript 中的匿名递归

Previous在工作中学习Next面向初学者的高阶组件介绍

Last updated 6 years ago

原文:

作者:Simeon Velichkov

(
  (
    (f) => f(f)
  )
  (
    (f) =>
      (l) => {
        console.log(l)
        if (l.length) f(f)(l.slice(1))
        console.log(l)
      }
  )
)
(
  [1, 2, 3]
)

是的,这就是想要分享给大家的一个有趣的示例。这个例子包含以下特性:[闭包]() 和 。

你可以复制粘贴上述代码到浏览器控制台,会看到打印结果如下:

[ 1, 2, 3 ]
[ 2, 3 ]
[ 3 ]
[]
[]
[ 3 ]
[ 2, 3 ]
[ 1, 2, 3 ]
(
  (
    (lambda (f) (f f))
    (lambda (f)
      (lambda (l)
        (print l)
        (if (not (null? l)) ((f f) (cdr l)))
        (print l)
      )
    )
  )
  '(1 2 3)
)

Unwind

像其他很多编程语言一样,函数调用是通过在函数名称后添加括号 () 来完成的:

function foo () { return 'hey' }
foo()

在 JavaScript 中我们可以使用括号包裹任意数量的表达式:

('hey', 2+5, 'dev.to')

上面代码返回结果是 'dev.to',原因是 JavaScript 返回最后一个表达式作为结果。

(function () { return 'hey' })

这本身并没有用处,因为匿名函数没有命名,无法被引用,除非在初始化的时候立即调用它。

就像是普通函数一样,我们可以在其后面添加括号 () 来进行调用。

(function () { return 'hey' })()

也可以使用箭头函数:

(() => 'hey')()

闭包

var foo = (hi) => (dev) => hi + ' ' + dev

在控制台调用上述函数会打印 hey dev.to:

foo('hey')('dev.to')

注意,我们可以在内部函数作用域访问外部函数的参数 hi。

以下代码跟上述代码一样:

function foo (hi) {
  return function (dev) { return hi + ' ' + dev }
}

自执行的版本如下:

(
  (hi) =>
    (
      (dev) => `${hi} ${dev}`
    )
    ('dev.to')
)
('hey')

首先,将 hey 作为参数 hi 的值传给最外层作用域的函数,然后这个函数返回另一个自执行函数。dev.to 作为参数 dev 的值传给内部函数,最后这个函数返回最终值:'hey dev.to'。

再深入一点

这个一个上述自执行函数的修改版本:

(
  (
    (dev) =>
      (hi) => `${hi} ${dev}`
  )
  ('dev.to')
)
('hey')

匿名递归

回到我们最初的例子,这次加点注释:

(
  (
    (f) => f(f) // 3.
  )
  (
    (f) => // 2.
      (l) => { // 4.
        console.log(l)
        if (l.length) f(f)(l.slice(1))
        console.log(l)
      }
  )
)
(
  [1, 2, 3] // 1.
)
  1. 输入函数 [1, 2, 3] 传给最外层作用域

  2. 整个函数作为参数传给上面函数

  3. 这个函数接收下面函数作为参数 f 的值,然后自身调用

  4. 2.将被调用被作为 3.的结果然后返回函数 4. ,该函数是满足最外层作用域的函数,因此接收输入数组作为 l 参数的值

至于结果为什么是那样子,原因是在递归内部有一个对函数 f 的引用来接收输入数组 l。所以能那样调用:

f(f)(l.slice(1))

注意,f 是一个闭包,所以我们只需要调用它就可以访问到操作输入数组的最里面的函数。

为了说明目的,第一个 console.log(l) 语句表示递归自上而下,第二个语句表示递归自下而上。

结论

希望你喜欢这篇文章,并从中学到了新的东西。闭包、自执行函数、函数式编程模式不是黑魔法。它们遵循一套易于理解和玩乐的简单原则。

话虽如此,你必须培养自己何时使用它们,何时不用的这样一种感觉。如果你的代码变得难以维护,那这可能会成为重构中一些好点子。

然而,理解这些基本技术对于创建清晰优雅的解决方案以及提升自我是至关重要的。

Happy Coding!

说到函数式编程,这里有一个使用 [Scheme]()) (JavaScript 借鉴过的其中一门语言)编写的类似例子:

使用括号 () 包裹一个匿名函数表示其结果就是 本身。

同样地,在匿名函数后添加括号 () 来执行函数,这被称为 。

[闭包]()) 指的是函数和该函数声明词法环境的组合。结合 ,我们可以定义如下:

需要注意的是, 和 [闭包]()) 用作初始化和封装状态,接下来我们来看另外一个例子。

Anonymous Recursion in JavaScript
https://en.wikipedia.org/wiki/Closure_(computer_programming)),[自执行函数](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression),[箭头函数](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions),[函数式编程](https://en.wikipedia.org/wiki/Functional_programming
匿名递归
https://en.wikipedia.org/wiki/Scheme_(programming_language
匿名函数
自执行函数
https://en.wikipedia.org/wiki/Closure_(computer_programming
箭头功能
自执行函数
https://en.wikipedia.org/wiki/Closure_(computer_programming