现在大部分公司的管理者都会通过技术面试来考察开发者的能力。如果你成为了候选人,那你一定需要去经历面试。
大部分的面试相信大家一定经历过,但是其实我们不应该过度关注于语法和一些怪异生僻的特性中。诚然这些能考察到候选人的基础功扎实与否,但我们应该更多的是从全局来考虑,询问架构和范例(这方面决定会对整个项目有重大的影响)。
语法和特性其实都能在谷歌中找到。但是软件工程师的智慧和JS开发者在经验中获得的通用范例和用法是无法从谷歌中获得的。
Javascript是特殊的,他现在几乎在所有大型应用中都扮演中重要的角色。那你又知道JS和其他的语言有什么不同之处吗?
接下来的几个问题会帮助你探索真正需要关注的内容:
1. 你能说出两个在Javascript中很重要的编程范式吗?
Javascript是多范式语言:命令式/过程式,函数式,面向对象。Javascript支持通过原型继承实现面向对象编程
优秀回答:
- 原型继承(或者原型)
- 函数式编程(闭包,一等函数,lambda演算,箭头函数)
红色预警: -不知道范式是什么,也没有提及原型面向对象或者函数式编程
参考:
2.什么是函数式编程
函数式编程,可以归结为面向过程的程序设计,但是结合了更多的数学计算的思想。函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程是JavaScript中的一个基本概念(JavaScript两大支柱之一(原型继承和函数式编程))
优秀回答:
- 纯函数
- 避免副作用
- 简单的函数功能组合
- 函数语言的例子:Lisp,ML,Haskell,Erlang,Clojure,Elm,F Sharp,OCaml等......
- 提到函数式编程的功能:一等函数,高阶函数,函数作为参数/值
红色预警:
- 没有提到纯函数,或者避免副作用
- 无法提供函数式编程语言的示例
- 无法识别启用函数式编程的JavaScript功能
3.类继承和原型继承有什么区别
类继承: JavaScript本来是没有类继承的,我们可以通过关键词new来将构造函数实例化,call和apply方法为类式继承提供了支持。通过改变this的作用环境,使得子类本身具有父类的各种属性。当然,我们现在拥有了ES6的calss,也可以使用calss来实现类继承。
原型继承: 实例直接从其他对象继承。实例通常通过工厂函数或“Object.create()”实例化。实例可以由许多不同的对象组成,从而可以方便地选择继承。
在JavaScript中,原型继承比类继承更简单、更灵活。
优秀回答:
- 类继承:紧密耦合或基于层级的分类
- 原型继承:提到了原型委托,功能继承,对象组合。
红色预警:
- 不依赖于原型继承和而类继承的组合
4.函数式编程与面向对象编程的优缺点是什么?
面向对象编程的优点: 很容易理解对象的基本概念,也很容易解释调用方法的含义。面向对象编程更倾向于命令式而不是声明式,声明式读起来就像是给计算机遵循的直接命令
面向对象编程的缺点: 面向对象编程通常依赖于共享状态。对象和行为通常连接在同一个实体上,该实体可以被任意数量的顺序不确定的函数随机访问,这可能导致不需要的行为,如竞争条件。
函数式编程的优点: 使用函数式编程,程序员可以避免任何共享状态或副作用,从而消除由于多个函数争夺相同资源而导致的错误。与面向对象编程相比,函数式编程从根本上简化,并易于重新组合,以获得更普遍可重用的代码。
函数式编程倾向于声明式和外延式,它们不详细说明操作的一步一步的指令,而是专注于做什么,让底层函数来处理如何做。这为重构和性能优化留下了巨大的空间,甚至允许你用更高效的算法替换整个算法,而只需很少的代码更改。
使用纯函数进行计算,也容易跨多个处理器或跨分布式计算集群扩展,而无需担心线程资源冲突、竞争条件等问题。
函数式编程的缺点: 过度使用函数式编程特性(如大型组合)可能会降低可读性,因为生成的代码通常更抽象、更简洁、更不具体。
与函数式编程相比,更多人熟悉面向对象编程和命令式编程,因此即使是函数式编程中的常见习惯用法也可能让新团队成员感到困惑。
函数式编程的学习曲线要比面向对象编程陡峭得多,因为面向对象编程的广泛流行使面向对象编程的语言和学习材料变得更加语义化,而函数式编程的语言则趋向于更加学术化和形式化。函数式编程概念经常涉及到使用lambda演算、代数和范畴理论中的习惯用法和符号,所有这些都需要去了解这些领域的知识。
优秀回答:
- 提到共享状态的问题,竞争相同资源的不同事物等等......
- 意识到函数式编程能够从根本上简化许多应用程序。
- 意识到学习曲线的差异。
- 阐明副作用以及它们如何影响程序的可维护性。
- 意识到功能强大的代码库可能具有陡峭的学习曲线。
- 意识到与同等的函数式编程代码库相比,过多的OOP代码会非常难以应对变化并且非常脆弱。
红色预警:
- 无法列出一种风格或另一种风格的缺点 - 任何一种风格都有其不足。
5. 什么时候适合选择类继承?
答案是啥时候都不适合,多层次的类结构是反模式。 优秀回答:
- 很少,几乎从来没有,或从未
- 单级别的时候有时候是可以的,比如React.Component
- 对象继承比类继承更好
6. 什么时候适合原型继承?
原型继承有多种类型:
- 原型链
- mixins,Object.assign()
- 函数式(别和函数式编程混淆,这里是创建闭包函数实现私有状态/封装)
每种类型的原型继承都有自己的创建方法,但是它们合成对象的能力都一样,它创建了has-a或uses-a或can-do关系,而不是类继承创建的is-a的关系。
优秀回答:
- 在模块或函数编程没有提供明显解决方案的情况下
- 当需要从多个地方组合对象时
- 任何需要继承的地方
红色预警:
- 不知道何时使用原型。
- 没有意识到mixins或
Object.assign()
7. 对象继承比类继承更好是为什么
代码重用应该通过将较小的功能单元组装到新对象中来实现,而不是继承类并创建对象分类法。
换句话说,使用can-do,has-a或使用关系而不是is-a关系。 优秀回答:
- 避免使用类层次结构
- 避免脆弱的类问题
- 避免紧密耦合
- 避免严格的分类(强制is-a关系对于新的代码使用是不友好的)
- 避免大猩猩香蕉问题(“你想要的是香蕉,你得到的是拿着香蕉的大猩猩,以及整个丛林”)
- 使代码更灵活
红色预警:
- 上面的一个都不知道
8. 什么是双向数据绑定和单向数据流,它们有何不同?
双向数据绑定意味着View层数据变化,Model层的数据也会相应变化,反之亦然。
单向数据流意味着Model层数据变化会引起View层数据变化,但View层数据变化无法引起Model层的数据变化,数据总是朝着一个方向流动(就像React里的store)。这使得理解起来更容易。
单向数据流具有是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。
优秀回答:
- React是单向数据流的范例,因此提及React是一个很好的信号。 Cycle.js是单向数据流的另一种流行实现。
- Angular/Vue是一种使用双向绑定的流行框架。
红色预警:
- 不了解任何一个意味着什么,无法阐明差异。
9.单体架构与微服务架构的优缺点是什么?
所谓的单体架构就是把所有的业务模块编写在一个项目中,最终会打包成一个war包,然后进行部署
微服务架构意味着应用程序由许多较小的独立应用程序组成,这些应用程序能够在自己的内存空间中运行,并且可能在许多不同的机器上相互独立地进行扩展和协作。
单体架构优点:
单片架构的主要优点是大多数应用程序可以共用很多东西,例如日志记录,速率限制以及审计跟踪和DOS保护等安全功能。
还有性能优势,因为共享内存访问比进程间通信(IPC)更快。
单体架构缺点: 随着应用程序的发展,应用程序服务往往会紧密耦合和纠缠,从而难以将服务隔离并独立扩展出来,并且实现代码可维护性等。 单体架构也很难理解,因为当你查看特定服务或控制器时,可能存在依赖性,副作用等不明显的隐形问题
它们还可以根据组织方式获得性能优势,因为可以独立于应用程序的其他部分,与核心服务隔离并对其进行伸缩。
微服务架构优点 微服务体系结构通常组织得更好,因为每个微服务都有一个非常特定的任务,并且不关心其他组件的任务。解耦的服务也更容易重新组合和配置,以满足不同应用程序的需要(例如,同时服务于web客户机和公共API)。
微服务架构缺点 微服务部署在虚拟机或容器上,导致VM争用工作激增。 运维复杂度提供
优秀回答:
- 微服务架构一开始成本较高,但微服务从长远来看往往会表现更好并且规模更大。
- 关于微服务和单片应用程序的实用性。两者在不同情况下的使用选择
红色预警:
- 不了解单体架构和微服务架构之间的差异。
- 对微服务的额外开销没有意识到。
- 不知道IPC和网络通信对微服务造成的额外性能开销。
- 无法明确解决单片应用程序的分离方式,以便在时机成熟时将它们轻松拆分为微服务。
- 低估了独立可扩展微服务的优势。
10. 什么是异步编程,为什么它在JavaScript中很重要?
同步编程意味着,除了条件和函数调用之外,代码从上到下依次执行,阻塞长时间运行的任务,如网络请求和磁盘I / O等。
异步编程意味着引擎在一个事件循环中运行。当需要阻塞操作时,将启动请求,并且代码将继续运行,而不会阻塞结果。当响应就绪时,将触发一个中断,这时运行一个事件处理程序,其中控制流将继续运行。通过这种方式,一个程序线程可以处理许多并发操作。
用户界面本质上是异步的,并且花费大部分时间等待用户输入来中断事件循环并触发事件处理程序。
默认情况下,Node是异步的,这意味着服务器的工作方式大致相同,在循环中等待网络请求,并在处理第一个请求时接受更多的传入请求。
这在JavaScript中很重要,因为它非常适合UI代码,并且非常有利于服务器上的性能。
优秀回答:
- 了解阻塞意味着什么,以及性能影响。
- 了解事件处理,以及为什么它对UI代码很重要。
红色预警:
- 不熟悉异步或同步的术语。
- 无法阐明性能影响或异步代码与UI代码之间的关系。