【译】每个JavaScript 开发者应该了解的10个面试题

889 阅读14分钟

原文地址

1. 你能说出两种对 JavaScript 应用开发者而言的编程范式吗?

JavaScript 是多范式语言,支持 命令式/程序式编程,以及 OOP(面向对象编程)和函数编程。JavaScript 通过原型继承支持 OOP。

希望听到:

  • 原型继承(还有:原型,OLOO)
  • 函数式编程(还有:闭包,第一等函数,lambadas(匿名函数))

红牌警告:

  • 没有范式概念,没有提到原型对象或函数编程

了解更多:

2. 什么是函数编程?

函数编程通过创建数学意义上的方法来避免共享状态和修改数据来生成程序。Lisp 是最早支持函数编程的,受到 lambda 的启发。Lisp 和 很多Lisp 家族语言至今仍然使用。

函数式编程是 JavaScript 基本概念。一些常见的函数工具在 ES5 中被添加。

希望听到:

  • 纯函数
  • 无副作用
  • 单一功能构成
  • 函数式语言:Lisp,ML,Haskell,Erlang,Clojure,Elm,F Sharp,OCaml等等
  • 提到支持函数式编程的特性:一等函数,高阶函数,作为参数/值的函数

红牌警告:

  • 没有提到纯函数和避免副作用
  • 不能提供函数式编程语言的例子
  • 不能识别 JavaScript 中函数式编程的特点

了解更多:

3. 类继承和原型继承的不同?

类继承:继承来自类(类似蓝图-类的一种描述),创建子类关系:等级分类法。通过构造函数的 new 关键字实例化。类继承可能在 ES6 中不允许使用 class 关键字。

原型继承:实例继承直接来自另一个对象。实例是通过工厂方法或者 Object.create()实现。实例可能从多个不同的对象中组成,允许容易地选择性继承。

在 JavaScript 中,原型继承比类继承更加简单和可扩展。

希望听到

  • 类:创建紧密耦合或者层次结构分类
  • 原型:提到链接继承,原型委托,函数继承,对象组合

红牌警告:

4. 函数式编程和面向对象编程的优缺点?

OOP优点:容易理解对象的基本概念,容易理解方法调用的意义。OOP 倾向使用命令式风格而不是声明式风格,它读起来像是直接指示计算机去做什么。

OOP缺点:OOP 典型地依赖共享状态。对象和行为被一个实体跟踪,也就是说任何数量的无序命令用函数可以随机访问,会导致像竞争条件一样的不可描述的行为。

FP优点:函数式范式,程序员可以避免共享状态和副作用,也就消灭了同个资源的多个函数计算引起的bug。这种特性是 point-free风格的可用性。相较于 OOP,函数倾向于快速简单而容易的重组通用的可复用代码。

函数式编程更喜欢声明和声明风格,它不会一步步指示操作,而是结合要做的事,让潜在的方法关系如何去做。这为重构和性能优化留下巨大的空间,甚至允许你替换全部算法只需要一点点代码改变,这种更有效的方式(比如:内存管理,使用懒惰评估替换有限评估)

利用纯函数的计算也很容易在多个处理器或跨分布式计算集群中扩展,而不必担心线程资源冲突,竞争条件等...

FP缺点:过度使用FP功能(如无点样式和大型组合)可能会降低可读性,因为生成的代码通常更抽象地指定,更简洁,更具体。

与函数式编程相比,更多人熟悉OO和命令式编程,因此即使是函数式编程中常见的习惯也会让新团队成员感到困惑。

FP比OOP有更陡峭的学习曲线,因为OOP的广泛流行使得OOP的语言和学习材料变得更具会话性,而FP的语言往往更具学术性和正式性。 FP概念经常被写成使用来自lambda演算,代数和类别理论的习语和符号,所有这些都需要在这些领域中的先验知识基础被理解。

希望听到

  • 提到共享状态的问题,竞争相同资源的不同事物等等......
  • 意识到FP能够从根本上简化许多应用程序。
  • 意识到学习曲线的差异。
  • 阐明副作用以及它们如何影响程序的可维护性。
  • 意识到功能强大的代码库可能具有陡峭的学习曲线。
  • 意识到与同等的FP代码库相比,高度OOP代码库可以非常耐受变化并且非常脆弱。
  • 意识到不可变性导致了一个极易访问和可延展的程序状态历史记录,允许轻松添加诸如无限撤消/重做,倒带/重放,时间旅行调试等功能。 可以在任一范例中实现不变性,但共享有状态对象的扩散使OOP中的实现变得复杂。

红牌警告:无法列出一种风格或另一种风格的缺点-有经验的人对任何风格都能说出一些限制。

了解更多

5. 什么时候类继承是一个合适的选择?

答案永远不会,或者几乎从不。 当然永远不会超过一个级别。 多级类层次结构是反模式。 多年来我一直在发出这个挑战,我听过的唯一答案属于几种常见的错误观念之一。 更常见的是,挑战是沉默。

“如果某个功能有时很有用 有时很危险 如果有更好的选择 然后总是使用更好的选择。“ -- Douglas Crockford

希望听到

  • 很少,几乎从来没有,或从未。
  • 从一个框架基类,如React.Component,单个级别有时是可以的。
  • “赞成对象组合而不是类继承。”

了解更多

6. 什么时候原型继承是一个合适的选择?

原型继承有多种类型:

  • 授权(即原型链)。
  • 连接(即mixins,Object.assign())。
  • 功能性(不要与函数式编程混淆。用于为私有状态/封装创建闭包的函数)。

每种类型的原型继承都有自己的一组用例,但它们在启用组合方面同样有用,它创建了有一个或用一个或可以做一个的关系而不是使用类继承创建是一个的关系。

希望听到

  • 在模块或函数编程没有提供明显解决方案的情况下。
  • 当需要从多个来源组合对象时。
  • 任何时候你需要继承。

红牌警告:

  • 不知道何时使用原型。
  • 没有意识到mixins或Object.assign()

了解更多

7.“赞成对象组合超过类继承”是什么意思?

这是“设计模式:可重用的面向对象软件的元素”的引用。 这意味着代码重用应该通过将较小的功能单元组装到新对象中而不是从类继承并创建对象分类来实现。

换句话说,使用can-do,has-a或 uses-a 关系而不是is-a关系。

希望听到:

  • 避免使用类层次结构。
  • 避免脆弱的基类问题。
  • 避免紧耦合。
  • 避免严格的分类(强制是一种新用例最终错误的关系)。
  • 避免大猩猩香蕉问题(“你想要的是香蕉,你得到的是拿着香蕉的大猩猩,以及整个丛林”)。
  • 使代码更灵活。

红牌警告:

  • 没有提到上述任何问题。
  • 未能阐明构成和阶级继承之间的差异,或组成的优势。

8.什么是双向数据绑定和单向数据流,它们有何不同?

双向数据绑定意味着 UI 表单被绑定,用来动态地建模数据,使得当 UI 表单改变时,模型数据随之改变,反之亦然。

单向数据流意味着模型是单一的事实来源。 UI中的更改触发消息,指示用户对模型的意图(或React中的“存储”)。 只有模型才有权更改应用程序的状态。 结果是数据总是在单一方向上流动,这使得它更容易理解。

单向数据流是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。

希望听到:

  • React是单向数据流的新规范示例,因此提及React是一个很好的信号。 Cycle.js是单向数据流的另一种流行实现。
  • Angular是一种使用双向绑定的流行框架。

红牌警告:

  • 不了解任何一个意味着什么。 无法阐明差异。

9.单一与微服务架构的优缺点是什么?

单一体系结构意味着您的应用程序是作为一个有凝聚力的代码单元编写的,其组件旨在协同工作,共享相同的内存空间和资源。

微服务架构意味着您的应用程序由许多较小的独立应用程序组成,这些应用程序能够在自己的内存空间中运行,并且可能在许多不同的机器上相互独立地进行扩展。

单一优点:单一架构的主要优势在于大多数应用程序通常都有大量的交叉问题,例如日志记录,速率限制以及审计跟踪和DOS保护等安全功能。

当一切都在同一个应用程序中运行时,很容易将组件与这些跨领域的问题联系起来。

还有性能优势,因为共享内存访问比进程间通信(IPC)更快。

单一架构缺点:随着应用程序的发展,单一应用程序服务往往会紧密耦合并纠缠在一起,因此很难将服务隔离,例如独立扩展或代码可维护性。

单一体系结构也很难理解,因为当您查看特定服务或控制器时,可能存在依赖性,副作用和魔法,这些并不明显。

微服务优点:微服务架构通常组织得更好,因为每个微服务都有非常特定的工作,并且不关心其他组件的工作。解耦服务也更容易重构和重新配置,以满足不同应用程序的需求(例如,同时提供Web客户端和公共API)。

它们还可以具有性能优势,具体取决于它们的组织方式,因为它可以隔离热服务并将其扩展为独立于应用程序的其余部分。

微服务缺点:当您构建新的微服务架构时,您可能会发现许多在设计时没有预料到的跨领域问题。一个单一的应用程序可以建立共享的魔术助手或中间件来处理这些跨领域的问题,而不需要太多的努力。

在微服务架构中,您需要为每个交叉问题产生单独模块的开销,或者将交叉问题封装在所有流量路由通过的另一个服务层中。

最终,即使是单一的体系结构也倾向于通过外部服务层来传输流量以解决交叉问题,但是采用单一体系结构,可以延迟这项工作的成本,直到项目更加成熟为止。

微服务经常部署在他们自己的虚拟机或容器上,导致VM争用工作激增。这些任务经常通过容器队列管理工具自动完成。

希望听到:

  • 尽管初始成本高于monolthic应用程序,但对微服务的积极态度。意识到微服务从长远来看往往会表现更好并且规模更大。
  • 关于微服务与单片应用程序的实用性。构建应用程序,以便服务在代码级别彼此独立,但在开始时很容易捆绑在一起作为单个应用程序。微服务开销成本可能会延迟,直到付出代价变得更加实际。

红牌警告:

  • 不了解单片和微服务架构之间的差异。
  • 对微服务的额外开销没有意识到或不切实际。
  • 不知道IPC和网络通信对微服务造成的额外性能开销。
  • 对微服务的缺点太负面了。无法明确解决单一应用程序的分离方式,以便在时机成熟时将它们轻松拆分为微服务。
  • 低估了独立可扩展微服务的优势。

10.什么是异步编程,为什么它在JavaScript中很重要?

同步编程意味着,除了条件和函数调用之外,代码从上到下依次执行,阻止长时间运行的任务,如网络请求和磁盘I / O.

异步编程意味着引擎在事件循环中运行。当需要阻塞操作时,将启动请求,代码将继续运行而不会阻塞结果。当响应准备就绪时,将触发中断,从而导致运行事件处理程序,控制流继续执行。通过这种方式,单个程序线程可以处理许多并发操作。

用户界面本质上是异步的,并且花费大部分时间等待用户输入来中断事件循环并触发事件处理程序。

默认情况下,节点是异步的,这意味着服务器的工作方式大致相同,在循环中等待网络请求,并在处理第一个请求时接受更多的传入请求。

这在JavaScript中很重要,因为它非常适合用户界面代码,并且非常有利于服务器上的性能。

希望听到:

  • 了解阻塞意味着什么,以及性能影响。
  • 了解事件处理,以及为什么它对UI代码很重要。

红牌警告:

  • 不熟悉异步或同步的术语。
  • 无法表达性能影响或异步代码与UI代码之间的关系。

结论

坚持高级主题。如果他们能够回答这些问题,那通常意味着他们有足够的编程经验可以在几周内获得语言怪癖和语法,即使他们没有很多JavaScript经验。

不要基于易于学习的东西(包括经典的CS-101算法或任何类型的拼图问题)取消候选人资格。

你真正需要知道的是,“这位候选人是否理解如何将应用程序放在一起?”

这就是口语采访。

在真实的采访中,我更加强调编码挑战和观察候选人代码。这些主题在我的“掌握JavaScript面试”系列中有详细介绍。

pic