对大部分公司来讲,招聘技术人员这种事情,管理层就应该放手交给技术团队,只有他们才可以准确地判断应聘者的技术实力。若是你恰巧是应聘者,你也是早晚都要去面试的。无论你是哪边的,都让大哥来教你几招。前端
大兄弟们,要收藏,也要点赞关注呐。
优秀的团队才是决定公司业绩的关键,一家公司要想于逆境之中仍能有所建树,最重要的就是得先培养出一只优秀的团队。面试
就像 Marcus Lemonis 说的,有三点(3 个 P)最重要:算法
员工(People),流程(Process),产品(Product)。
在创业初期,你招来的工程师必须是可以独当一面的大神队友。他最好可以帮着招聘工程师,能指导其它工程师,还能帮初级和中级工程师解决各类问题。这样优秀的队友,不管什么时候都多多益善。编程
要评估一个应聘者的真实水准,最佳方式就是结对编程(pair programming)。
和应聘者结对编程,一切都听应聘者的。多观察、多聆听,看看应聘者是个怎样的人。用微博的 API 抓取消息并显示在时间线上,就是个很好的考察应聘者的面试项目。设计模式
不过结对编程再好使,也没办法让你彻底了解一个应聘者。这个时候,面试也能帮上不少忙——可是千万别浪费时间去问一些语法(syntax)或者语言上的细节(language quirks)——问些高端的问题吧,大兄弟。问问项目架构(architecture),编程范式(paradigms),这个层面上的判断(the big desicions)可以在很大程度上影响一个项目的成败。数组
语法和语言特性(features)这种小知识,Google 一搜一大把,谁都会。而工程师在工做中所积累的软件工程方面的经验,以及我的经常使用的编程范式及代码风格(idioms),这些可都是很难 Google 到的宝贵财富。安全
JavaScript 很独特,它在各类大型项目中都起着相当重要的做用。那是什么让 JavaScript 如此不同凡响?性能优化
下面几个问题,也许能帮你一探究竟。网络
JavaScript 是一门多范式(multi-paradigm)的编程语言,它既支持命令式(imperative)/面向过程(procedural)编程,也支持面向对象编程(OOP,Object-Oriented Programming),还支持函数式编程(functional programming)。JavaScript 所支持的面向对象编程包括原型继承(prototypal inheritance)。闭包
函数式编程,是将数学函数组合起来,而且避免了状态共享(shared state)及可变数据(mutable data),由此而产生的编程语言。发明于 1958 年的 Lisp 就是首批支持函数式编程的语言之一,而 λ 演算(lambda calculus)则能够说是孕育了这门语言。即便在今天,Lisp 这个家族的编程语言应用范围依然很广。
函数式编程但是 JavaScript 语言中很是重要的一个概念(它但是 JavaScript 的两大支柱之一)。ES5 规范中就增长了不少经常使用的函数式工具。
类继承(Class Inheritance):实例(instances)由类继承而来(类和实例的关系,能够类比为建筑图纸和实际建筑 🏠 的关系),同时还会建立父类—子类这样一种关系,也叫作类的分层分类(hierarchical class taxonomies)。一般是用 new
关键字调用类的构造函数(constructor functions)来建立实例的。不过在 ES6 中,要继承一个类,不用 class
关键字也能够。
原型继承(Prototypal Inheritance):实例/对象直接从其它对象继承而来,建立实例的话,每每用工厂函数(factory functions)或者 Object.create()
方法。实例能够从多个不一样的对象组合而来,这样就能选择性地继承了。
在 JavaScript 中,原型继承比类继承更简单,也更灵活。
面向对象编程的优势:关于“对象”的一些基础概念理解起来比较容易,方法调用的含义也好解释。面向对象编程一般使用命令式的编码风格,声明式(declarative style)的用得比较少。这样的代码读起来,像是一组直接的、计算机很容易就能遵循的指令。
面向对象编程的不足:面向对象编程每每须要共享状态。对象及其行为经常会添加到同一个实体上,这样一来,若是一堆函数都要访问这个实体,并且这些函数的执行顺序不肯定的话,极可能就会出乱子了,好比竞争条件(race conditions)这种现象(函数 A 依赖于实体的某个属性,可是在 A 访问属性以前,属性已经被函数 B 修改了,那么函数 A 在使用属性的时候,极可能就得不到预期的结果)。
函数式编程的优势:用函数式范式来编程,就不须要担忧共享状态或者反作用了。这样就避免了几个函数在调用同一批资源时可能产生的 bug 了。拥有了“无参风格”(point-free style,也叫隐式编程)之类的特性以后,函数式编程就大大简化了,咱们也能够用函数式编程的方式来把代码组合成复用性更强的代码了,面向对象编程可作不到这一点。
函数式编程更偏心声明式、符号式(denotational style)的编码风格,这样的代码,并非那种为了实现某种目的而须要循序渐进地执行的一大堆指令,而是关注宏观上要作什么。至于具体应该怎么作,就都隐藏在函数内部了。这样一来,要是想重构代码、优化性能,那就大有可为了。(译者注:以作一道菜为例,就是由 买菜
-> 洗菜
-> 炒菜
这三步组成,每一步都是函数式编程的一个函数,无论作什么菜,这个流程都是不会变的。而想要优化这个过程,天然就是要深刻每一步之中了。这样无论内部如何重构、优化,总体的流程并不会变,这就是函数式编程的好处。)甚至能够把一种算法换成另外一种更高效的算法,同时还基本不须要修改代码(好比把及早求值策略(eager evaluation)替换为惰性求值策略(lazy evaluation))。
利用纯函数进行的计算,能够很方便地扩展到多处理器环境下,或者应用到分布式计算集群上,同时还不用担忧线程资源冲突、竞争条件之类的问题。
函数式编程的不足:代码若是过分利用了函数式的编程特性(如无参风格、大量方法的组合),就会影响其可读性,从而简洁度有余、易读性不足。
大部分工程师仍是更熟悉面向对象编程、命令式编程,对于刚接触函数式编程的人来讲,即便只是这个领域的一些的简单术语,均可能让他怀疑人生。
函数式编程的学习曲线更陡峭,由于面向对象编程太普及了,学习资料太多了。相比而言,函数式编程在学术领域的应用更普遍一些,在工业界的应用稍逊一筹,天然也就不那么“平易近人”了。在探讨函数式编程时,人们每每用 λ 演算、代数、范畴学等学科的专业术语和专业符号来描述相关的概念,那么其余人想要入门函数式编程的话,就得先把这些领域的基础知识搞明白,能不让人头大么。
千万别用类继承!或者说尽可能别用。若是非要用,就只用它继承一级(one level)就行了,多级的类继承简直就是反模式的。这个话题(不太明白是关于什么的……)我也参与讨论过好些年了,仅有的一些回答最终也沦为 常见的误解 之一。更多的时候,这个话题讨论着讨论着就没动静了。
若是一个特性有时候颇有用
但有时候又很危险
而且还有另外一种更好的特性能够用
那 务必要用另外一种更好的特性
~ Douglas Crockford
React.Component
。原型继承能够分为下面几类:
Object.assign()
)上面这三种原型继承都有各自的适用场景,不过它们都颇有用,由于都能实现组合继承(composition),也就是创建了 A 拥有特性 B(has-a)、A 用到了特性 B(uses-a) 或者 A 能够实现特性 B(can-do) 的这样一种关系。相比而言,类继承创建的是 A 就是 B 这样一种关系。
Object.assign()
。这句话引用的是《设计花纹》(Design Patterns,设计模式)这本书的内容。意思是要想实现代码重用,就应该把一堆小的功能单元组合成知足需求的各类对象,而不是经过类继承弄出来一层一层的对象。
换句话说,就是尽可能编程实现 can-do、has-a 或者 uses-a 这种关系,而不是 is-a 这种关系。
双向数据绑定(two-way data binding),意味着 UI 层所呈现的内容和 Model 层的数据动态地绑定在一块儿了,其中一个发生了变化,就会马上反映在另外一个上。好比用户在前端页面的表单控件中输入了一个值,Model 层对应该控件的变量就会马上更新为用户所输入的值;反之亦然,若是 Modal 层的数据有变化,变化后的数据也会马上反映至 UI 层。
单向数据流(one-way data flow), 意味着只有 Model 层才是单一数据源(single source of truth)。UI 层的变化会触发对应的消息机制,告知 Model 层用户的目的(对应 React 的 store
)。只有 Model 层才有更改应用状态的权限,这样一来,数据永远都是单向流动的,也就更容易了解应用的状态是如何变化的。
采用单向数据流的应用,其状态的变化是很容易跟踪的,采用双向数据绑定的应用,就很难跟踪并理解状态的变化了。
采用单体架构(monolithic architecture)的应用,各组件的代码是做为一个总体存在的,组件之间互相合做,共享内存和资源。
而微服务架构(microservice architecture)则是由许许多多个互相独立的小应用组成,每一个应用都有本身的内存空间,应用在扩容时也是独立于其它应用进行的。
单体架构的优点:大部分应用都有至关数量的横切关注点(cross-cutting concerns),好比日志记录,流量限制,还有审计跟踪和 DOS 防御等安全方面的需求,单体架构在这方面就颇有优点。
当全部功能都运行在一个应用里的时候,就能够很方便地将组件与横切关注点相关联。
单体架构也有性能上的优点,毕竟访问共享内存仍是比进程间通讯(inter-process communication,IPC)要快的。
单体架构的劣势:随着单体架构应用功能的不断开发,各项服务之间的耦合程度也会不断增长,这样一来就很难把各项服务分离开来了,要作独立扩容或者代码维护也就更不方便了。
微服务的优点:微服务架构通常都有更好的组织结构,由于每项服务都有本身特定的分工,并且也不会干涉其它组件所负责的部分。服务解耦以后,想要从新组合、配置来为各个不一样的应用提供服务的话,也更方便了(好比同时为 Web 客户端和公共 API 提供服务)。
若是用合理的架构来部署微服务的话,它在性能上也是颇有优点的,由于这样一来,就能够很轻松地分离热门服务,对其进行扩容,同时还不会影响到应用中的其它部分。
微服务的劣势:在实际构建一个新的微服务架构的时候,会遇到不少在设计阶段没有预料到的横切关注点。若是是单体架构应用的话就很简单,新建一个中间件(shared magic helpers 不知道怎么翻译……)来解决这样的问题就好了,没什么麻烦的。
可是在微服务架构中就不同了,要解决这个问题,要么为每一个横切关注点都引入一个独立的模块,要么就把全部横切关注点的解决方案封装到一个服务层中,让全部流量都从这里走一遍就好了。
为了解决横切关注点的问题,虽然单体架构也趋向于把全部的路由流量都从一个外部服务层走一遍,可是在这种架构中,能够等到项目很是成熟以后再进行这种改造,这样就能够把还这笔技术债的时间尽可能日后拖一拖。
微服务通常都是部署在虚拟机或容器上的,随着应用规模的不断增长,虚拟机抢工做(VM wrangling work)的状况也会迅速增长。任务的分配通常都是经过容器群(container fleet)管理工具来自动实现的。
在同步编程中,代码会按顺序自顶向下依次执行(条件语句和函数调用除外),若是遇到网络请求或者磁盘读/写(I/O)这类耗时的任务,就会堵塞在这样的地方。
在异步编程中,JS 运行在事件循环(event loop)中。当须要执行一个阻塞操做(blocking operation)时,主线程发起一个(异步)请求,(工做线程就会去执行这个异步操做,)同时主线程继续执行后面的代码。(工做线程执行完毕以后,)就会发起响应,触发中断(interrupt),执行事件处理程序(event handler),执行完后主线程继续日后走。这样一来,一个程序线程就能够处理大量的并发操做了。
用户界面(user interface,UI)自然就是异步的,大部分时间它都在等待用户输入,从而中断事件循环,触发事件处理程序。
Node.js 默认是异步的,采用它构建的服务端和用户界面的执行机制差很少,在事件循环中等待网络请求,而后一个接一个地处理这些请求。
异步在 JavaScript 中很是重要,由于它既适合编写 UI,在服务端也有上佳的性能表现。
多问问应聘者高层次的知识点,若是能讲清楚这些概念,就说明即便应聘者没怎么接触过 JavaScript,也可以在短短几个星期以内就把语言细节和语法之类的东西弄清楚。
不要由于应聘者在一些简单的知识上表现不佳就把对方 pass 掉,好比经典的 CS-101 算法课,或者一些解谜类的题目。
面试官真正应该关注的,是应聘者是否知道如何把一堆功能组织在一块儿,造成一个完整的应用。
推荐:你们能够关注我,私信发送‘架构’便可获取如下资料,里面有源码分析、性能优化、微服务架构、工程化、分布式等知识点。走的就是高端路线 下图是资料的一部分知识点 有用没用一看就知道的: