每一个 JavaScript 工程师都应当晓得的 10 个面试题

原文链接:10 Interview Questions Every JavaScript Developer Should Know

对大部份公司来讲,雇用手艺人员这类事变,管理层就应当松手交给手艺团队,只要他们才能够正确地推断应聘者的手艺气力。假如你恰巧是应聘者,你也是早晚都要去口试的。不论你是哪边的,都让老大来教你几招。

大兄弟们,要珍藏,也要点赞呐。

以人为本

How to Build a High Velocity Development Team 一文中,我提出了一些看法,我以为这些看法很主要,所以在这里再反复一遍:

优异的团队才是决议公司业绩的症结,一家公司要想于困境当中仍能有所竖立,最主要的就是得先培养出一只优异的团队。

就像 Marcus Lemonis 说的,有三点(3 个 P)最主要:

员工(People),流程(Process),产物(Product)。

在创业早期,你招来的工程师必需是能够独当一面的大神队友。他最好能够帮着雇用工程师,能指点别的工程师,还能帮低级和中级工程师处置惩罚种种问题。如许优异的队友,不管什么时刻都多多益善。

要想晓得口试应聘者时,有哪些罕见的注重事项,能够读读 Why Hiring is So Hard in Tech 这篇文章。

要评价一个应聘者的实在水准,最好体式格局就是结对编程(pair programming)。

和应聘者结对编程,一切都听应聘者的。多视察、多倾听,看看应聘者是个怎样的人。用微博的 API 抓取音讯并显现在时刻线上,就是个很好的考核应聘者的口试项目。

不过结对编程再好使,也没办法让你完全相识一个应聘者。这个时刻,口试也能帮上许多忙——然则万万别浪费时刻去问一些语法(syntax)或许言语上的细节(language quirks)——问些高端的问题吧,大兄弟。问问项目架构(architecture),编程范式(paradigms),这个层面上的推断(the big desicions)能够在很大程度上影响一个项目标成败。

语法和言语特征(features)这类小学问,Google 一搜一大把,谁都邑。而工程师在事情中所积聚的软件工程方面的履历,以及个人经常运用的编程范式及代码作风(idioms),这些可都是很难 Google 到的宝贵财富。

JavaScript 很奇特,它在种种大型项目中都起着至关主要的作用。那是什么让 JavaScript 云云异乎寻常?

下面几个问题,或许能帮你一探终究。

1. 能说出来两种关于 JavaScript 工程师很主要的编程范式么?

JavaScript 是一门多范式(multi-paradigm)的编程言语,它既支撑敕令式(imperative)/面向历程(procedural)编程,也支撑面向对象编程(OOP,Object-Oriented Programming),还支撑函数式编程(functional programming)。JavaScript 所支撑的面向对象编程包含原型继承(prototypal inheritance)。

口试加分项

  • 原型继承(即:原型,OLOO——链接到别的对象的对象);
  • 函数式编程(即:闭包(closure),一类函数(first class functions),lambda 函数:箭头函数)。

口试减分项

  • 连范式都不晓得,更别提什么原型 OO(prototypal oo)或许函数式编程了。

深切相识

2. 什么是函数式编程?

函数式编程,是将数学函数组合起来,而且防止了状况同享(shared state)及可变数据(mutable data),由此而发生的编程言语。发明于 1958 年的 Lisp 就是首批支撑函数式编程的言语之一,而 λ 演算(lambda calculus)则能够说是孕育了这门言语。纵然在本日,Lisp 这个家属的编程言语运用局限依旧很广。

函数式编程但是 JavaScript 言语中异常主要的一个观点(它但是 JavaScript 的两大支柱之一)。ES5 范例中就增添了许多经常运用的函数式东西。

口试加分项

  • 纯函数(pure functions)/函数的地道性(function purity)
  • 晓得怎样防止副作用(side-effects)
  • 简朴函数的组合
  • 函数式编程言语:Lisp,ML,Haskell,Erlang,Clojure,Elm,F#,OCaml,等等
  • 提到了 JavaScript 言语中支撑函数式编程(FP)的特征:一类函数,高阶函数(higher order functions),作为参数(arguments)/值(values)的函数

口试减分项

  • 没有提到纯函数,以及怎样防止副作用
  • 没有供应函数式编程言语的例子
  • 没有说是 JavaScript 中的哪些特征使得函数式编程得以完成

深切相识

3. 类继承和原型继承有什么区分?

类继承(Class Inheritance):实例(instances)由类继承而来(类和实例的关联,能够类比为修建图纸和现实修建 🏠 的关联),同时还会竖立父类—子类如许一种关联,也叫做类的分层分类(hierarchical class taxonomies)。平常是用 new 症结字挪用类的构造函数(constructor functions)来竖立实例的。不过在 ES6 中,要继承一个类,不必 class 症结字也能够。

原型继承(Prototypal Inheritance):实例/对象直接从别的对象继承而来,竖立实例的话,每每用工场函数(factory functions)或许 Object.create() 要领。实例能够从多个差别的对象组合而来,如许就可以挑选性地继承了。

在 JavaScript 中,原型继承比类继承更简朴,也更天真。

口试加分项

  • 类:会竖立严密的耦合,或许说层级构造(hierarchies)/分类(taxonomies)。
  • 原型:提到了连接继承(concatenative inheritance)、原型托付( prototype delegation)、函数继承(functional inheritance),以及对象组合(object composition)。

口试减分项

  • 原型继承和组合,与类继承比拟,不晓得哪一个更好。

深切相识

4. 函数式编程和面向对象编程,各有什么长处和不足呢?

面向对象编程的长处:关于“对象”的一些基础观点邃晓起来比较轻易,要领挪用的寄义也好诠释。面向对象编程平常运用敕令式的编码作风,声明式(declarative style)的用得比较少。如许的代码读起来,像是一组直接的、盘算机很轻易就可以遵照的指令。

面向对象编程的不足:面向对象编程每每须要同享状况。对象及其行动常常会增添到统一个实体上,如许一来,假如一堆函数都要接见这个实体,而且这些函数的实行递次不确定的话,极能够就会出乱子了,比方合作前提(race conditions)这类征象(函数 A 依赖于实体的某个属性,然则在 A 接见属性之前,属性已被函数 B 修正了,那末函数 A 在运用属性的时刻,极能够就得不到预期的效果)。

函数式编程的长处:用函数式范式来编程,就不须要忧郁同享状况或许副作用了。如许就防止了几个函数在挪用统一批资本时能够发生的 bug 了。具有了“无参作风”(point-free style,也叫隐式编程)之类的特征以后,函数式编程就大大简化了,我们也能够用函数式编程的体式格局来把代码组合成复用性更强的代码了,面向对象编程可做不到这一点。

函数式编程更偏幸声明式、标记式(denotational style)的编码作风,如许的代码,并非那种为了完成某种目标而须要循序渐进地实行的一大堆指令,而是关注宏观上要做什么。至于详细应当怎样做,就都隐藏在函数内部了。如许一来,如果想重构代码、优化机能,那就大有可为了。(译者注:以做一道菜为例,就是由 买菜 -> 洗菜 -> 炒菜 这三步构成,每一步都是函数式编程的一个函数,不论做什么菜,这个流程都是不会变的。而想要优化这个历程,自然就是要深切每一步当中了。如许不论内部怎样重构、优化,团体的流程并不会变,这就是函数式编程的优点。)以至能够把一种算法换成另一种更高效的算法,同时还基础不须要修正代码(比方把尽早求值战略(eager evaluation)替换为惰性求值战略(lazy evaluation))。

应用纯函数举行的盘算,能够很方便地扩展到多处置惩罚器环境下,或许运用到分布式盘算集群上,同时还不必忧郁线程资本争执、合作前提之类的问题。

函数式编程的不足:代码假如太过应用了函数式的编程特征(如无参作风、大批要领的组合),就会影响其可读性,从而简约度有余、易读性不足。

大部份工程师照样更熟习面向对象编程、敕令式编程,关于刚打仗函数式编程的人来讲,纵然只是这个领域的一些的简朴术语,都能够让他疑心人生。

函数式编程的进修曲线更峻峭,由于面向对象编程太提高了,进修材料太多了。比拟而言,函数式编程在学术领域的运用更普遍一些,在工业界的运用稍逊一筹,自然也就不那末“平易近民”了。在议论函数式编程时,人们每每用 λ 演算、代数、领域学等学科的专业术语和专业标记来形貌相干的观点,那末其他人想要入门函数式编程的话,就得先把这些领域的基础学问搞邃晓,能不让人头大么。

口试加分项

  • 同享状况的瑕玷、资本合作、等等(面向对象编程)
  • 函数式编程能够极大地简化运用开辟
  • 面向对象编程和函数式编程进修曲线的差别
  • 两种编程体式格局各自的不足之处,以及对代码后期保护带来的影响
  • 函数式作风的代码库,进修曲线会很峻峭
  • 面向对象编程作风的代码库,修正起来很难,很轻易出问题(和程度相称的函数式作风的代码比拟)
  • 不可变性(immutability),能够极大地提拔顺序汗青状况(program state history)的可见性(accessible)和扩展性(malleable),如许一来,想要增添诸如无穷打消/重做、倒带/回放、可退却的调试之类的功用的话,就简朴多了。不论是面向对象编程照样函数式编程,这两种范式都能完成不可变性,然则要用面向对象来完成的话,同享状况对象的数目就会剧增,代码也会变得复杂许多。

口试减分项

  • 没有讲这两种编程范式的瑕玷——假如熟习最少个中一种范式的话,应当能够说出许多这类范式的瑕玷吧。

深切相识

老是你俩,看来你俩真是异常主要啊。

5. 什么时刻该用类继承?

万万别用类继承!或许说只管别用。假如非要用,就只用它继承一级(one level)就好了,多级的类继承几乎就是反形式的。这个话题(不太邃晓是关于什么的……)我也介入议论过好些年了,唯一的一些回复终究也沦为 罕见的误会 之一。更多的时刻,这个话题议论着议论着就没动静了。

假如一个特征有时刻很有效

但有时刻又很风险

而且另有另一种更好的特征能够用


务必要用另一种更好的特征

~ Douglas Crockford

口试加分项

  • 只管别用,以至是完全不必类继承。
  • 有时刻只继承一级的话也照样 OK 的,比方从框架的基类继承,比方 React.Component
  • 比拟类继承,对象组合(object composition)更好一些。

深切相识

6. 什么时刻该用原型继承?

原型继承能够分为下面几类:

  • 托付(delegation,也就是原型链)
  • 组合(concatenative,比方混用(mixins)、Object.assign()
  • 函数式(functional,这个函数式原型继承不是函数式编程。这里的函数是用来竖立一个闭包,以完成私有状况(private state)或许封装(encapsulation))

上面这三种原型继承都有各自的实用场景,不过它们都很有效,由于都能完成组合继承(composition),也就是竖立了 A 具有特征 B(has-a)、A 用到了特征 B(uses-a) 或许 A 能够完成特征 B(can-do) 的如许一种关联。比拟而言,类继承竖立的是 A 就是 B 如许一种关联。

口试加分项

  • 晓得在什么状况下不合适用模块化(modules)或许函数式编程。
  • 晓得须要组合多个差别泉源的对象时,应当怎样做。
  • 晓得什么时刻该用继承。

口试减分项

  • 不晓得什么时刻应当用原型。
  • 不晓得混用和 Object.assign()

深切相识

7. 为何说“对象组合比类继承更好”?

这句话援用的是《设想斑纹》(Design Patterns,设想形式)这本书的内容。意义是要想完成代码重用,就应当把一堆小的功用单位组合成满足需求的种种对象,而不是经由过程类继承弄出来一层一层的对象。

换句话说,就是只管编程完成 can-dohas-a 或许 uses-a 这类关联,而不是 is-a 这类关联。

口试加分项

  • 防止运用类继承。
  • 防止运用问题多多的基类。
  • 防止紧耦合。
  • 防止极为不天真的条理分类(taxonomy)(类继承所发生的 is-a 关联能够会致使许多误用的状况)
  • 防止大猩猩香蕉问题(“你只是想要一根香蕉,效果末了却整出来一只拿着香蕉的大猩猩,另有全部森林”)。
  • 要让代码更具扩展性。

口试减分项

  • 没有提到上面任何一种问题。
  • 没有表达清晰对象组合与类继承有什么区分,也没有提到对象组合的长处。

深切相识

8. 双向数据绑定/单向数据流的寄义和区分

双向数据绑定(two-way data binding),意味着 UI 层所显现的内容和 Model 层的数据动态地绑定在一起了,个中一个发生了变化,就会马上反应在另一个上。比方用户在前端页面的表单控件中输入了一个值,Model 层对应当控件的变量就会马上更新为用户所输入的值;反之亦然,假如 Modal 层的数据有变化,变化后的数据也会马上反应至 UI 层。

单向数据流(one-way data flow), 意味着只要 Model 层才是单一数据源(single source of truth)。UI 层的变化会触发对应的音讯机制,示知 Model 层用户的目标(对应 React 的 store)。只要 Model 层才有变动运用状况的权限,如许一来,数据永远都是单向活动的,也就更轻易相识运用的状况是怎样变化的。

采纳单向数据流的运用,其状况的变化是很轻易跟踪的,采纳双向数据绑定的运用,就很难跟踪并邃晓状况的变化了。

口试加分项

  • React 是单向数据流的典范,口试时提到这个框架的话会加分。Cycle.js 则是另一个很盛行的单向数据流的库。
  • Angular 则是双向数据绑定的典范。

口试减分项

  • 不邃晓单向数据流/双向数据绑定的寄义,也说不清晰两者之间的区分。

深切相识

9. 单体架构和微效劳架构各有何好坏?

采纳单体架构(monolithic architecture)的运用,各组件的代码是作为一个团体存在的,组件之间相互合作,同享内存和资本。

而微效劳架构(microservice architecture)则是由许许多多个相互自力的小运用构成,每一个运用都有本身的内存空间,运用在扩容时也是自力于别的运用举行的。

单体架构的上风:大部份运用都有相称数目的横切关注点(cross-cutting concerns),比方日记纪录,流量限定,另有审计跟踪和 DOS 防护等平安方面的需求,单体架构在这方面就很有上风。

当一切功用都运行在一个运用里的时刻,就可以够很方便地将组件与横切关注点相干联。

单体架构也有机能上的上风,毕竟接见同享内存照样比历程间通讯(inter-process communication,IPC)要快的。

单体架构的劣势:跟着单体架构运用功用的不停开辟,各项效劳之间的耦合程度也会不停增添,如许一来就很难把各项效劳星散开来了,要做自力扩容或许代码保护也就更不方便了。

微效劳的上风:微效劳架构平常都有更好的构造构造,由于每项效劳都有本身特定的分工,而且也不会过问别的组件所担任的部份。效劳解耦以后,想要重新组合、设置来为各个差别的运用供应效劳的话,也更方便了(比方同时为 Web 客户端和大众 API 供应效劳)。

假如用合理的架构来布置微效劳的话,它在机能上也是很有上风的,由于如许一来,就可以够很轻松地星散热点效劳,对其举行扩容,同时还不会影响到运用中的别的部份。

微效劳的劣势:在现实构建一个新的微效劳架构的时刻,会碰到许多在设想阶段没有预料到的横切关注点。假如是单体架构运用的话就很简朴,新建一个中间件(shared magic helpers 不晓得怎样翻译……)来处置惩罚如许的问题就好了,没什么贫苦的。

然则在微效劳架构中就不一样了,要处置惩罚这个问题,要么为每一个横切关注点都引入一个自力的模块,要么就把一切横切关注点的处置惩罚方案封装到一个效劳层中,让一切流量都从这里走一遍就好了。

为了处置惩罚横切关注点的问题,虽然单体架构也趋向于把一切的路由流量都从一个外部效劳层走一遍,然则在这类架构中,能够比及项目异常成熟以后再举行这类革新,如许就可以够把还这笔手艺债的时刻只管今后拖一拖。

微效劳平常都是布置在虚拟机或容器上的,跟着运用范围的不停增添,虚拟机抢事情(VM wrangling work)的状况也会敏捷增添。使命的分派平常都是经由过程容器群(container fleet)管理东西来自动完成的。

口试加分项

  • 关于微效劳的积极态度,虽然初始本钱会比单体架构要高一些。晓得微效劳的机能和扩容在历久看来表现更佳。
  • 在微效劳架构和单体架构运用上都有实战履历。能够使运用中的各项效劳在代码层面相互自力,然则又能够在开辟早期敏捷地将各项效劳打包成一全部的单体架构运用。微效劳化的革新能够在运用相称成熟以后,革新本钱在可蒙受局限内的时刻再举行。

口试减分项

  • 不晓得单体架构和微效劳架构的区分。
  • 不晓得微效劳架构分外的开支,或许没有现实履历。
  • 不晓得微效劳架构中,IPC 和收集通讯所致使的分外的机能开支。
  • 太过诽谤微效劳。说不清晰什么时刻应当把单体架构运用解耦成微效劳。
  • 低估了可自力扩容的微效劳的上风。

10. 异步编程是什么?又为何在 JavaScript 中这么主要?

在同步编程中,代码会按递次自顶向下顺次实行(前提语句和函数挪用除外),假如碰到收集要求或许磁盘读/写(I/O)这类耗时的使命,就会梗塞在如许的处所。

在异步编程中,JS 运行在事宜轮回(event loop)中。当须要实行一个壅塞操纵(blocking operation)时,主线程提议一个(异步)要求,(事情线程就会去实行这个异步操纵,)同时主线程继承实行背面的代码。(事情线程实行终了以后,)就会提议相应,触发中缀(interrupt),实行事宜处置惩罚顺序(event handler),实行完后主线程继承今后走。如许一来,一个顺序线程就可以够处置惩罚大批的并发操纵了。

用户界面(user interface,UI)自然就是异步的,大部份时刻它都在守候用户输入,从而中缀事宜轮回,触发事宜处置惩罚顺序。

Node.js 默许是异步的,采纳它构建的效劳端和用户界面的实行机制差不多,在事宜轮回中守候收集要求,然后一个接一个地处置惩罚这些要求。

异步在 JavaScript 中异常主要,由于它既合适编写 UI,在效劳端也有上佳的机能表现。

口试加分项

  • 邃晓壅塞的寄义,以及对机能带来的影响。
  • 邃晓事宜处置惩罚顺序,以及它为何对 UI 部份的代码很主要。

口试减分项

  • 不熟习同步、异步的观点。
  • 讲不清晰异步代码和 UI 代码的机能影响,也说不邃晓它俩之间的关联。

总结

多问问应聘者高条理的学问点,假如能讲清晰这些观点,就申明纵然应聘者没怎样打仗过 JavaScript,也能够在短短几个礼拜以内就把言语细节和语法之类的东西弄清晰。

不要由于应聘者在一些简朴的学问上表现不佳就把对方 pass 掉,比方典范的 CS-101 算法课,或许一些解谜类的问题。

口试官真正应当关注的,是应聘者是不是晓得怎样把一堆功用构造在一起,构成一个完全的运用。

电话口试的注重点就这些了,在线下的口试中,我越发关注应聘者现实编写代码的才能,我会视察他怎样写代码。在我的《通晓 JavaScript 口试》这个系列文章中,会有更深切的形貌。

    原文作者:samsara0511
    原文地址: https://segmentfault.com/a/1190000014143796
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞