JavaScript 很特殊,它几乎在每个大型应用程序中都扮演着至关重要的角色。如果你是一名JavaScript程序员,以下是一些可以帮助您探索真正重要内容的问题。
对 JavaScript 应用程序开发人员很重要的编程范式有哪些?
JavaScript 是一种多范式语言,支持命令式、过程式编程以及OOP(面向对象编程)和函数式编程。JavaScript 支持具有原型继承的OOP 。
什么是函数式编程?
函数式编程通过组合数学函数来生成程序,并避免共享状态和可变数据。Lisp(于 1958 年指定)是最早支持函数式编程的语言之一,并且深受 lambda 演算的启发。Lisp 和许多 Lisp 家族语言今天仍然被广泛使用。
函数式编程是一种基于函数的编程范式,其中函数被视为一等公民。函数可以作为参数传递给其他函数,也可以作为返回值返回。函数式编程鼓励使用不可变数据和无副作用的函数。这意味着函数只依赖于它的输入,并且不会更改任何外部状态。在JavaScript中,函数式编程可以使用高阶函数、闭包和递归等概念来实现。
经典继承和原型继承有什么区别?
类继承:实例继承自类(如蓝图——类的描述),并创建子类关系:层次类分类法。实例通常通过带有“new”关键字的构造函数实例化。类继承可能会也可能不会使用ES6 中的class关键字。
原型继承:实例直接从其他对象继承。实例通常通过工厂函数或Object.create() 实例化。实例可以由许多不同的对象组成,允许简单的选择性继承。
在 JavaScript 中,原型继承比类继承更简单、更灵活。
函数式编程与面向对象编程的优缺点是什么?
OOP优点:容易理解对象的基本概念,容易理解方法调用的含义。OOP 倾向于使用命令式风格而不是声明式风格,它读起来就像一组直接的指令供计算机遵循。
OOP 缺点: OOP 通常依赖于共享状态。对象和行为通常在同一个实体上捆绑在一起,可以由任意数量的具有不确定顺序的函数随机访问,这可能导致不良行为,例如竞争条件。
FP 的优点:使用函数范式,程序员可以避免任何共享状态或副作用,从而消除多个函数竞争相同资源而导致的错误。与 OOP 相比,借助无点风格(又名默认编程)等功能,功能往往会被彻底简化并轻松重组为更普遍可重用的代码。
FP 也倾向于支持声明式和指称式风格,这些风格不会详细说明操作的分步说明,而是专注于做什么,让底层函数负责如何操作。这为重构和性能优化留下了巨大的空间,甚至允许您用更高效的算法替换整个算法,而只需很少的代码更改。(例如,memoize 或使用惰性求值代替急切求值。)
使用纯函数的计算也很容易跨多个处理器或跨分布式计算集群进行扩展,而不必担心线程资源冲突、竞争条件等……
FP 缺点:过度利用 FP 特性(例如无点样式和大型组合)可能会降低可读性,因为生成的代码通常更抽象地指定、更简洁且更不具体。
与函数式编程相比,更多人熟悉OO和命令式编程,因此即使是函数式编程中的常见习语也会让新团队成员感到困惑。
FP 的学习曲线比 OOP 陡峭得多,因为 OOP 的广泛流行使得 OOP 的语言和学习材料变得更具会话性,而 FP 的语言往往更加学术和正式。FP 概念经常写成关于使用 lambda 演算、代数和范畴论中的习语和符号,所有这些都需要在这些领域有先验知识基础才能理解。
什么时候经典继承是合适的选择?
答案是从不,或者几乎从不。当然永远不会超过一个级别。多级类层次结构是一种反模式。
什么时候原型继承是合适的选择?
原型继承的类型不止一种:
- 委托(即原型链)。
- 串联(即 mixins,Object.assign())。
- 函数式(不要与函数式编程混淆。用于为私有状态/封装创建闭包的函数)。
每种类型的原型继承都有自己的一组用例,但它们在启用组合方面同样有用,组合创建了has-a或uses-a或can-do关系,而不是 is -a关系使用类继承创建。
“对象组合优先于类继承”是什么意思?
这意味着代码重用应该通过将更小的功能单元组装到新对象中来实现,而不是从类继承和创建对象分类法。
换句话说,使用can-do、has-a或uses-a关系,而不是is-a关系。
什么是双向数据绑定和单向数据流,它们有何不同?
双向数据绑定意味着 UI 字段动态绑定到模型数据,这样当 UI 字段更改时,模型数据也随之更改,反之亦然。
数据流的一种方式意味着模型是唯一的事实来源。UI 中的更改会触发消息,这些消息会向模型发出用户意图信号(或 React 中的“存储”)。只有模型有权更改应用程序的状态。效果是数据总是单向流动,这样更容易理解。
数据流的一种方式是确定性的,而双向绑定会导致难以理解和理解的副作用。
单体架构与微服务架构的优缺点是什么?
单体架构意味着您的应用程序被编写为一个内聚的代码单元,其组件旨在协同工作,共享相同的内存空间和资源。
微服务架构意味着您的应用程序由许多较小的、独立的应用程序组成,这些应用程序能够在自己的内存空间中运行并在可能的许多独立机器上相互独立地扩展。
整体式优点:整体式架构的主要优点是大多数应用程序通常具有大量横切关注点,例如日志记录、速率限制和安全功能(例如审计跟踪和 DOS 保护)。
当一切都通过同一个应用程序运行时,很容易将组件连接到那些横切关注点。
还可能有性能优势,因为共享内存访问比进程间通信 (IPC) 更快。
整体式缺点:随着应用程序的发展,整体式应用程序服务往往会紧密耦合和纠缠在一起,因此很难为独立扩展或代码可维护性等目的隔离服务。
单体架构也更难理解,因为可能存在依赖关系、副作用和魔法,当您查看特定服务或控制器时,这些并不明显。
微服务优点:微服务架构通常组织得更好,因为每个微服务都有一个非常具体的工作,并且不关心其他组件的工作。分离的服务也更容易重组和重新配置以服务于不同应用程序的目的(例如,同时服务于 Web 客户端和公共 API)。
它们还可以具有性能优势,具体取决于它们的组织方式,因为可以隔离热门服务并独立于应用程序的其余部分扩展它们。
微服务缺点:在构建新的微服务架构时,您可能会发现许多在设计时没有预料到的横切关注点。一个单一的应用程序可以建立共享的魔法助手或中间件来处理这样的横切问题而不需要太多努力。
在微服务架构中,您需要为每个横切关注点承担单独模块的开销,或者将横切关注点封装在所有流量都经过的另一个服务层中。
最终,即使是单体架构也倾向于通过外部服务层路由流量以实现横切关注点,但使用单体架构,可以延迟这项工作的成本,直到项目更加成熟。
微服务经常部署在自己的虚拟机或容器上,导致 VM 争论工作激增。这些任务经常通过集装箱车队管理工具实现自动化。
什么是异步编程,为什么它在 JavaScript 中很重要?
同步编程意味着,除了条件和函数调用,代码从上到下按顺序执行,阻塞长时间运行的任务,如网络请求和磁盘 I/O。
异步编程意味着引擎在事件循环中运行。当需要阻塞操作时,请求被启动,代码继续运行而不阻塞结果。当响应就绪时,将触发一个中断,这会导致运行一个事件处理程序,控制流将在此处继续。这样,单个程序线程可以处理许多并发操作。
用户界面本质上是异步的,大部分时间都在等待用户输入来中断事件循环并触发事件处理程序。
默认情况下,Node 是异步的,这意味着服务器以大致相同的方式工作,循环等待网络请求,并在处理第一个请求时接受更多传入请求。
这在 JavaScript 中很重要,因为它非常适合用户界面代码,并且对服务器性能非常有益。