[置顶] 每个JavaScript开发人员都应该知道的10道面试题

时间:2023-02-15 03:24:28
JavaScript十分特别,并且几乎在每个大型应用中起着至关重要的作用。那么,到底是什么使JavaScript显得与众不同,意义非凡?

这里有一些问题将帮助你了解其真正的奥妙所在:

 

1、你能说出对JavaScript应用开发者很重要的两种编程范式吗?

答:

JavaScript是一门多范式语言, 支持命令式/过程式编程以及OOP(面向对象编程)和函数式编程。JavaScript使用原型继承来实现OOP。

面试官很高兴听到这些:

·原型继承(或:原型,OLOO(对象链接到其他对象)模式)。

·函数式编程(或:闭包,First-class Function,lambda)。

警告:

·不知道什么是范式,没有提及原型对象或函数式编程。

了解更多:

·TheTwo Pillars of JavaScript Part 1 — PrototypalOO.

·TheTwo Pillars of JavaScript Part 2 — FunctionalProgramming.

 

2、什么是函数式编程?

答:

函数式编程是一种编程范式。在函数式编程语言中,函数是第一类的对象,也就是说,函数不依赖于任何其他的对象而可以独立存在,而在面向对象的语言中,函数 ( 方法 ) 是依附于对象的,属于对象的一部分。这一点决定了函数在函数式语言中的一些特别的性质,比如作为传出/传入参数,作为一个普通的变量等。Lisp是最早支持函数式编程的语言。

函数式编程是JavaScript的一个基本概念(JavaScript的两大支柱之一)。几种常见的功能特性在ES5后被添加到JavaScript中。

面试官很高兴听到这些:

·纯函数/函数纯度。

·避免边界效应。

·简单的函数组合。

·函数式语言的例子:Lisp,ML,Haskell,Erlang,Clojure,Elm,F Sharp,OCaml等等。

·提到支持函数式编程功能的特性:第一类函数,高阶函数,函数作为参数/值。

警告:

·没有提及纯函数/避免边界效应。

·不能说出函数式编程语言的例子。

·不了解JavaScript支持函数式编程的特性。

了解更多:

·TheTwo Pillars of JavaScript Part 2.

·TheDao of Immutability.

·ProfessorFrisby’s Mostly Adequate Guide to Functional Programming.

·TheHaskell School of Music.

 

3、类继承和原型继承之间的区别?

答:

类继承:实例继承类,并创建子类之间的关系:类层次分类。实例通过构造函数实例化,通常用“new”关键字。类继承可用也可不用从ES6的“class”关键字。

原型继承:实例直接从其他对象继承。实例通常使用工厂函数或“Object.create()”来初始化。实例可以由许多不同的对象组成的,允许轻松地选择继承。

在JavaScript中,原型继承比类继承更灵活、简单。

面试官很高兴听到这些:

·类:类继承多了会产生紧耦合,层次结构/分类。

·原型:提到衔接继承,原型委托,函数继承,对象组成。

警告:

·一定要偏爱原型继承和组合胜过类继承。

了解更多:

·TheTwo Pillars of JavaScript Part 1 — PrototypalOO.

·CommonMisconceptions About Inheritance in JavaScript.

 

4、函数式编程与面向对象编程相比有什么优点和缺点?

答:

面向对象优点:我们很容易理解对象的基本概念,容易理解的方法调用的含义。 OOP倾向于使用命令式的风格,而不是声明的风格,倒像是一个直接让计算机使用的指令集。

面向对象缺点:面向对象通常依赖于共享状态。对象和行为通常附着在相同的实体,这使得它可以被任意多个函数随机访问,且这种访问顺序是非确定性的,这可能导致不希望看到的行为,例如竞争条件。

函数式编程优点:使用函数范式,程序员可以避免任何共享状态或边界效应,从而消除由多个函数竞争相同的资源带来的错误。相比面向对象编程,由于*点方式(又名默契/隐性编程)的可用性这一特性,对于更一般的可重用代码来说,函数往往可以从根本上简化和方便地被重构。

函数式编程倾向于声明性的风格,它不需要用算法来明确的指出每一步该怎么做(命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。),而是告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。这给重构和性能优化留下了巨大的空间,甚至允许你以非常少却更高效的代码来替换整个算法。(例如,记忆化,或使用惰性求值代替急切求值。)

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

函数式编程缺点:过度开发函数式编程的功能,如经常使用*点方式以及大规模组合可能会减少代码可读性,因为生成的代码往往比较抽象,简洁。

比起函数式编程,更多的人熟悉面向对象和命令式编程 。所以新加入的成员可能在一些习惯用法上会造成混淆。

函数式编程比面向对象编程的学习曲线更加陡峭,因为面向对象的广泛普及使得面向对象的语言和学习材料变得更加熟络,而函数式编程的语言往往更加学术和正式。函数式编程概念经常使用习惯用语和符号的演算,代数和范畴论等来描述,所有这一切都需要在这些领域的现有知识基础来理解。

面试官很高兴听到这些:

·提到共享状态,不同的事情争夺相同的资源等等麻烦...

·意识到函数式编程的能力,从根本上简化了许多应用。

·意识到函数式编程和面向对象编程在学习曲线上的不同。

·表达边界效应以及它如何影响程序的可维护性。

·认识到一个功能强大的代码库会有一个陡峭的学习曲线。

·认识到一个高度面向对象的代码库会是非常难以改变,很脆弱,相比于FP代码库。

警告:

·无法列出的一种风格或其他风格的缺点 - 任何人都经历了两种风格应该已经碰到了一些限制。

了解更多:

·TheTwo Pillars of JavaScript Part 1 — PrototypalOO.

·TheTwo Pillars of JavaScript Part 2 — FunctionalProgramming.

 

5、何时选择类继承是正确的?

答:这是一个有趣的问题。答案是永远不会。多年来我一直在发出这一挑战,我听过的答案分为几种常见的误解。更常见的则是遭遇沉默。

“If afeature is sometimes useful and sometimes dangerous and if there is a betteroption then always use the better option.” ~ Douglas Crockford

面试官很高兴听到这些:

·很少,几乎没有,或者永远。

·“比起类继承我更喜欢对象组合”。

警告:

·其他答案

·“React Components”——不,类继承的缺陷并不因一个新框架的出现和拥抱“class”这个关键字而改变。你并不需要为了使用React而使用“class”。这个回答揭示了你对“class”和React的误解。

了解更多:

·TheTwo Pillars of JavaScript Part 1 — PrototypalOO.

·JS Objects — Inherited aMess.

 

6、何时选择原型继承是正确的?

答:

原型继承有以下几种类型:

·委托(即原型链)。

·衔接(Mixins,“Object.assign()”)。

·函数(不要和函数式编程混淆。函数用于创建一个私有状态/封装的闭包)。

每种类型的原型继承都具有自己使用情况,但所有这些在它们的组合能力上同样有用,都创建has-a或uses-a或can-do关系,而类继承创建的是is-a关系。

面试官很高兴听到这些:

·在模块或函数式编程不提供明显的解决方案的情况下。

·当你需要编写来自多个来源的对象时。

·当你需要继承时。

警告:

·对什么时候使用原型搞不清楚。

·不认识Mixins或“Object.assign()”

了解更多:

·“ProgrammingJavaScript Applications”: Prototypes section.


7、“比起类继承更喜欢对象组合”是什么意思?

答:这是从“设计模式:可复用面向对象软件的基础”书中的引用。这意味着代码重用应由装配更小单位的功能到新对象来实现,而不是继承类和创建对象分类来实现。

换句话说就是:用has-a或uses-a或can-do关系来代替is-a关系。

面试官很高兴听到这些:

·避免类层次结构。

·避免脆弱的基类问题。

·避免紧耦合。

·避免僵化的分类(对新的使用情况来说,强迫is-a关系最终是错误的)。

·避免大猩猩的香蕉问题(“你想要的是一个香蕉,但你得到的是一个大猩猩拿着香蕉,整个丛林”)。

·使代码更灵活。

警告:

·没有提到上述的任何点。

·不能够清晰地表达组成和类继承的区别,或组合物的优点。

了解更多:

·composition overinheritance及实例分析

 

8、什么是双向数据绑定和单向数据流,以及它们之间的区别?

答:

双向数据绑定意味着UI字段动态绑定到模型数据上,当一个UI字段变化,它的模型数据将改变,反之亦然。

单向数据流意味着该模型是唯一的来源。只有模型才有改变应用程序状态的权力。其效果是,数据总是在一个单一的方向流动,这使得它更容易理解。

单向数据流是确定性的,而双向绑定可引起副作用而令人更难把握和理解。

面试官很高兴听到这些:

·React是单向数据流的新的典型例子,因此提到React是一个好消息,当然你也可以使用ReactLink在React中实现双向绑定。Cycle.js是单向数据流的另一种流行的实现。

·Angular是一种使用双向绑定的流行框架。

警告:

·不了解这两种是什么东西。无法表达它们之间的差异。

了解更多:

·Two-WayBinding Helpers

 

9、整体架构与微服务架构各自的利弊?

答:

整体架构意味着你的应用程序被写成一个由代码拼成的整体,其组件被设计成协同工作,共享相同的存储空间和资源。

微服务架构意味着你的应用程序是由大量的能够在自己的内存空间中运行,相互独立缩放,跨越潜在的多个独立的机器。

整体架构优点:大多数应用程序通常有大量的横切关注点,如日志记录,速率限制,审计跟踪和DOS防护等安全功能。当一切都运行在同一个应用程序,它很容易连接到这些横切关注点的组件。另一个优点就是共享内存访问比进程间通信(IPC)更快。

整体架构缺点:随着应用服务不断更新,其往往会更加紧密地耦合和纠缠在一起。因此很难有目的地分离服务,如独立的缩放或代码可维护性。整体架构也很难理解,因为一个特定的服务或控制器可能有依赖,副作用和不可预知因素,这是不明显的。

微服务架构优点:微服务架构有着更好的组织,因为每个微服务具有非常具体的工作,并且不涉及其它组分的作业。分开的服务也更容易重新组合和重新配置,以满足不同的应用程序的目的(例如,服务于Web客户端和公共API)。他们还可以有性能优势,这取决于他们是如何组织的,因为它可以隔离热服务和缩放它们独立于应用程序的其他部分。

微服务架构缺点:当你正在构建一个新的微服务架构,你可能会发现大量的交叉问题,这在设计时是你没有预料到。而一种整体架构应用程序可以建立共享的帮助者或中间件来毫不费力地处理横切关注。在微服务架构中,你要么为每个横切关注点产生单独模块的开销,或在另一个服务层中封装横切关注点,以使所有业务都通过。

最后,即使整体架构倾向于通过一个外部服务层来解决交叉关注的问题,但这有可能延误了这项工作的费用,直到该项目更为成熟。

微服务经常被部署在自己的虚拟机或容器中,导致虚拟机放牧式工作的激增。这些任务常常用容器管理工具来做自动化。

面试官很高兴听到这些:

·对微服务架构的积极态度,尽管较高的初始成本VS 整体架构应用程序。意识到从长远来看微服务往往运作性和伸缩性更好。

·自己有在微服务架构和整体架构方面的实践。微服务架构中的服务在代码级别是相互独立的,在一开始就比整体架构应用容易捆绑在一起。微服务架构管理成本可以被延迟,直到它变得更切合实际的支付价钱。

警告:

·不知道整体架构和微服务架构之间的差异。

·不了解或不切实际的知道有关微服务架构的额外开销。

·不了解IPC和微服务网络通信的所造成的额外性能开销。

·关于微服务的弊端太消极。无法说出在时机成熟时将整体架构分裂成微服务的方式。

·低估了独立可伸缩微服务的优势。

 

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

答:

同步编程意味着,限制的条件和功能调用,代码依次从顶至底执行时,阻塞在长期运行的任务,如网络请求和磁盘I/O。

异步编程意味着发动机在事件循环运行。当需要阻塞操作,请求被启动,并且代码不断不会阻塞的结果运行。当响应准备就绪,中断发射,这将导致一个事件处理程序来运行,其中,控制流继续。以这种方式,单个程序线程可处理许多并发操作。

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

节点本质上是异步的,这意味着服务器工作在大致相同的方式,在等待一个网络请求的环路,并在第1个被处理时可以接受更多到来的请求。

这在JavaScript中是很重要的,因为它非常自然地适合用户接口代码,并对服务器性能非常有益。

面试官很高兴听到这些:

·了解阻塞和性能的含义。

·了解事件处理,以及它对用户界面代码的重要性。

警告:

·不熟悉异步或同步术语。

·无法说出性能的含义或异步代码与UI代码之间的关系。

了解更多:

·什么是 Event Loop?

·JavaScript运行机制详解:再谈Event Loop

·同步与异步的概念

 

结论

面试结束。坚持高水平的主题。如果你们能回答这些问题,这通常意味着你们有足够的编程经验在几个星期内掌握语言的怪癖和语法,即使你们没有很多的JavaScript编程经验。

作为面试官,请不要用基础知识的不扎实来取消面试者的资格,那些都太容易学了。

你真正需要知道的是,“面试者是否了解如何把一个应用程序拼凑在一起吗?”