[访谈实录】一年半的前端 Bigo 1、2、3 访谈实录
前言
笔者其实是三月份就面的 Bigo
,当时工作经验算一年半多。之所以现在才发,其实是之前虽然总结了,但多半是自己总结归纳复盘用,有点粗糙,现在重新整理,希望对大家有所帮助
一面
说说 Javascript
的数据类型
最新的 ECMAScript
标准 定义了 8 种数据类型
原始数据类型:Boolean、Null、Undefined、Number、BigInt、String、Symbol
引用类型:对象(Object
)。其中对象类型包括:数组(Array
)、函数(Function
)、还有两个特殊的对象:正则(RegExp
)和日期(Date
)
加分项:BigInt
和 Symbol
类型
BigInt
是一种数字类型的数据,它可以表示任意精度格式的整数。由于 Number
类型的局限性。Number 类型的局限性(JavaScript 中的 Number
是双精度浮点型,这意味着精度有限,如下所示)
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
max + 1 // 9007199254740992
max + 2 // 9007199254740992
注意 max + 1 === max + 2
,这是不对的
BigInt
就是解决此类问题
Symbol
类型的使用场景
Symbol
: 表示独一无二的值,通过 Symbol
函数生成,接收一个字符串作为参数,表示对 Symbol
实例的描述,主要是为了在控制台显示
应用场景:Symbol
的目的就是为了实现一个唯一不重复不可变的值,任何一个 Symbol
都是唯一的,不会和其他任何 Symbol
相等
- 对象中保证不同的属性名
- 注意:使用
Symbol
值定义属性的时候,必须放在方括号中 - 读取的时候也是不能使用点运算符
- 注意:使用
- 定义一组常量,保证这组常量都是不相等的
- 使用 Symbol 定义类的私有属性/方法
const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass{
// 公有方法
foo(baz) {
this[bar](baz);
}
// 私有方法
[bar](baz) {
return this[snaf] = baz; //私有属性
}
// ...
};
-
Vue
中的 project 和 inject
其他应该留意的知识点:
-
注意属性名的遍历
- 遍历对象的时候,该属性不会出现在 for...in...、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回
- 可以通过 Object. getOwnPropertySymbols()
-
Symbol.hasInstance
- 指向一个内部方法。当其他对象使用 instanceof 运算符,判断是否为该对象的实例的时候,会调用这个方法
- 比如,foo instanceof Foo在语言内部,实际调用的是Foo[Symbol.hasInstance](foo),有点类似劫持了 instanceof 方法
class MyClass {
[Symbol.hasInstance](foo) {
return foo instanceof Array;
}
}
[1, 2, 3] instanceof new MyClass() // true
null
和 undefined
的区别
null
类型代表着空值,代表着一个空指针对象,typeof
null
会是得到 'object'
所以可以认为它是一个特殊的对象值。undefined
当你声明一个变量未初始化的时候,得到的就是 undefined
-
typeof
的值不一样
console.log(typeof undefined); //undefined
console.log(typeof null); //object
- 转为数值时,值不一样
console.log(Number(undefined)); //NaN
console.log(undefined + 10);//NaN
console.log(Number(null)); //0
console.log(null + 10); //10
- === 运算符可区分
null
和undefined
-
null
使用的场景- 作为对象原型链的终点
Object.getPrototypeOf(Object.prototype) // null
- 作为对象原型链的终点
-
undefined
的典型用法 【变量,函数参数,函数返回,对象属性】
常见的页面性能优化
HTTP 上有哪些优化手段
重排和重绘的概念,以及如何避免重排和重绘
TCP 三次握手和 TCP 四次握手的区别
关于TCP的握手机制,一定不要死记硬背,要理解为什么这么设计,也就很容易记住了
三次握手:
-
在客户端和服务器之间建立正常的TCP网络连接时,客户端首先会发出一个
SYN
消息,服务器使用SYN+ACK
应答表示已经接收到这个消息,最后客户端再以ACK
消息响应。这样在客户端和服务器之间才能建立起可靠的TCP
连接,数据才可以在客户端和服务器之间传递 -
建立连接时,客户端发送
SYN
包到服务器,等待服务器响应。(SYN
同步序列编号,是建立连接时使用的握手信号) -
服务器收到
SYN
包,使用ACK
包进行确认应答,同时自己也会发送一个SYN
包,即发送SYN+ACK
包。 客户端收到服务器的SYN
包,向服务器发送确认包ACK
。此包发送完毕,代表TCP
连接完成,完成了三次握手
四次挥手:四次挥手是释放 TCP
连接的握手过程
- 客户端向服务端发送释放连接报文
FIN
,等待服务端确认,并停止发送数据 - 服务器收到连接释放请求后,发送
ACK
包表示确认。(此状态下,表示客户端到服务器的连接已经释放,不再接受客户端发的数据了,但是服务器要是还发送数据,客户端依然接收) - 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文
FIN
,等待客户端确认。 - 客户端收到服务器连接释放报文后,发出
ACK
包表示确认。此时客户端会进入TIME_WAIT
状态,该状态将持续2MSL
(最大报文段生存时间,指报文段在网络中生存的时间,超时将被抛弃)时间,若该时间段内没有服务器重发请求的话,就进入关闭状态,当服务端接收到ACK
应答后,立即进入关闭状态
webpack 性能优化怎么做的?
小程序的数据更新是怎样的?
小程序有哪些线程?
- 小程序的渲染层和逻辑层分别由 2 个线程管理:渲染层的界面使用了
WebView
进行渲染,逻辑层采用 JsCore 线程运行 JS 脚本 - 逻辑层:创建一个单独的线程去执行 JavaScript,在这个环境下执行的都是有关小程序业务逻辑的代码,由于 js 不跑在
WebView
里,就不能直接操纵 DOM 和 BOM,这就是小程序没有 window 全局变量的原因 - 渲染层:界面渲染相关的任务全都在
WebView
线程里执行,通过逻辑层代码去控制渲染哪些界面。一个小程序存在多个界面,所以渲染层存在多个WebView
线程 - 双线程通信【Virtual DOM 相信大家都已有了解,大概是这么个过程:用JS对象模拟DOM树 -> 比较两棵虚拟DOM树的差异 -> 把差异应用到真正的DOM树上。】
1、在渲染层把 WXML 转化成对应的 JS 对象
2、在逻辑层发生数据变更的时候,通过宿主环境提供的 setData 方法把数据从逻辑层传递到 Native,再转发到渲染层
3、经过对比前后差异,把差异应用在原来的 DOM 树上,更新界面
小程序多次设置很多数据的时候会对性能产生很大的影响么?怎么优化
有,可以统一一次设置,多次尽量最后一次设置
Vue 2.x
的实现原理?
回答了双向数据绑定的相关原理
Vue 的 diff 算法
Vue 的 compile 过程
- 主要是三个过程
parse
,optimize
,generate
- compile 的作用是解析模板,生成渲染模板的 render。而 render 的作用,也是为了生成跟模板节点一一对应的 Vnode
- parse: 接收 template 原始模板,按照模板的节点 和数据 生成对应的 ast【通过大量的正则匹配去实现对字符串的解析】
- Optimize:遍历递归每一个ast节点,标记静态的节点(没有绑定任何动态数据),这样就知道那部分不会变化,于是在页面需要更新时,减少去比对这部分DOM。从而达到性能优化的目的。【为什么要有优化过程,因为我们知道 Vue 是数据驱动,是响应式的,但是我们的模板并不是所有数据都是响应式的,也有很多数据是首次渲染后就永远不会变化的,那么这部分数据生成的 DOM 也不会变化,我们可以在 patch 的过程跳过对他们的比对。】
- Generate:把前两步生成完善的 ast 组装成 render 字符串(这个 render 变成函数后是可执行的函数,不过现在是字符串的形态,后面会转成函数)
Vuex简单的原理实现【同时考察了发布订阅的模式】
参考:面试题:谈谈你对对vuex的理解
移动端有哪些兼容的场景【重点回顾】
- 1px 问题解决:www.jianshu.com/p/3a262758a…
- 防止手机中页面放大和缩小
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
- 上下拉动滚动条时卡顿、慢。Android3+和iOSi5+支持CSS3的新属性为overflow-scrolling
body {
-webkit-overflow-scrolling:touch;
overflow-scrolling:touch;
}
- ios和android下触摸元素时出现半透明灰色遮罩
-webkit-tap-highlight-color:rgba(255,255,255,0);
- click 的
300ms
延迟问题
问题:在移动端中,click事件是生效的,但是,点击之后会有300ms的延迟响应
原因:safari是最早做出这个机制的,因为在移动端里,浏览器需要等待一段时间来判断此次用户操作是单击还是双击,所以就有click 300ms 的延迟机制
方案一:禁用缩放
当 HTML
文档头部包含如下 meta
标签时:表明这个页面是不可缩放的,那双击缩放的功能就没有意义了,此时浏览器可以禁用默认的双击缩放行为并且去掉 300ms
的点击延迟
<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,maximum-scale=1">
方案二:更改默认的视口宽度
<meta name="viewport" content="width=device-width">
如果设置了上述meta标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了 这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。
方案三:引入 fastclick
库来解决
FastClick
的实现原理是在检测到 touchend
事件的时候,会通过 DOM
自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
参考:www.jianshu.com/p/b5c103a9b…
-
水平居中和垂直居中
-
node有使用过么
二面
HTTP 中的 content-encoding
Content-Encoding
是一个实体消息首部,用于对特定媒体类型的数据进行压缩。当这个首部出现的时候,它的值表示消息主体进行了何种方式的内容编码转换。这个消息首部用来告知客户端应该怎样解码才能获取在 Content-Type
中标示的媒体类型内容。
Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br
参考:developer.mozilla.org/zh-CN/docs/…
HTTP
中的长连接
HTTP
协议采用“请求-应答”模式,当使用普通模式,即非 KeepAlive
模式时,每个请求/应答客户和服务器都要新建一个连接,完成 之后立即断开连接(HTTP
协议为无连接的协议);当使用 Keep-Alive
模式(又称持久连接、连接重用)时,Keep-Alive
功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive
功能避免了建立或者重新建立连接
http 1.0
中默认是关闭的,需要在 http
头加入 "Connection: Keep-Alive"
,才能启用 Keep-Aliv
e;http 1.1
中默认启用 Keep-Alive
,如果加入 "Connection: close ",才关闭。目前大部分浏览器都是用 http1.1 协议,也就是说默认都会发起 Keep-Alive 的连接请求了,所以是否能完成一个完整的 Keep- Alive
连接就看服务器设置情况
参考:blog.****.net/gt11799/art… developer.mozilla.org/zh-CN/docs/…
说下 JS 中的事件循环
强缓存和协商缓存
启发缓存有了解过么
- 没有任何关于缓存的字段 —— 不设置任何缓存策略
- 常会取响应头中的
Date
减去Last-Modified
值的 10% 作为缓存时间
参考:
- 浏览器缓存策略
V8 中的垃圾回收机制
参考:【Web技术】737- 深入理解 Chrome V8垃圾回收机制
node 的全局变量有哪些
JavaScript
中有一个特殊的对象,称为全局对象(Global Object
),它及其所有属性都可以在程序的任何地方访问,即全局变量
在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global
,所有全局变量(除了 global 本身以外)都是 global
对象的属性。
在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。 global,process,__filename,__dirname
参考:Node.js 全局对象
node 了解程度
ES6 有了解哪些?
set 和 map 有了解么?
map 的实现原理是什么?
Map
利用链表,hash
的思想来实现。
首先,Map可以实现删除,而且删除的数据可以是中间的值。而链表的优势就是在中间的任意位置添加,删除元素都非常快,不需要移动其他元素,直接改变指针的指向就可以。而在存储数据很多的情况下,会导致链条过长,导致查找效率慢,所以我们可以创建一个桶(存储对象的容器),根据 hash
(把散列的值通过算法变成固定的某值)来平局分配数据,防止链条过长。
参考:
- ES6 Map 原理分析
- ES6 Map 原理
为什么要想着离职呢?
算法题
输入[1,3,1,3,2],输出数组中唯一一个只存在一项的值,比如如上就是 2
三面
谈谈你的项目【其中针对项目进行了提问】
说说 webpack 打包优化
从一个数组中拿到前三个最小的值
一道编程题
[1,2,3,1,2,4,1,3,2,1] [1,2] [3,4,5] =>[3,4,5,3,3,4,5,4,1,3,2,1]
说明:第一个输入是原始数组,第二个输入是一个条件:要在原始数组中的连续数组,第三个输入是要替换掉原数组符合参数二条件的数据
能够使用的api:
1. 数组的长度获取。
2. 数组某个下标对应的数字。
3. 往数组里push元素。
总结
归纳总结了 Bigo 一二三面的知识,有一些问题总结回答建议,有一些没有,希望对大家有帮助
参考
-
JavaScript 中的表示任意精度的 BigInt
-
小程序的线程架构
-
小程序学习笔记-双线程
-
【Vue原理】Compile - 白话版
-
移动端页面兼容性问题解决方案整理(一)
-
关于移动端适配,你必须要知道的
-
移动端webApp兼容问题解决
推荐阅读
-
纯干货分享 | 研发效能提升——敏捷需求篇-而敏捷需求是提升效能的方式中不可或缺的模块之一。 云智慧的敏捷教练——Iris Xu近期在公司做了一场分享,主题为「敏捷需求挖掘和组织方法,交付更高业务价值的产品」。Iris具有丰富的团队敏捷转型实施经验,完成了企业多个团队从传统模式到敏捷转型的落地和实施,积淀了很多的经验。 这次分享主要包含以下2个部分: 第一部分是用户影响地图 第二部分是事件驱动的业务分析Event driven business analysis(以下简称EDBA) 用户影响地图,是一种从业务目标到产品需求映射的需求挖掘和组织的方法。 在软件开发过程中可能会遇到一些问题,比如大家使用不同的业务语言、技术语言,造成角色间的沟通阻碍,还会导致一些问题,比如需求误解、需求传递错误等;这会直接导致产品的功能需求和要实现的业务目标不是映射关系。 但在交付期间,研发人员必须要将这些需求实现交付,他们实则并不清楚这些功能需求产生的原因是什么、要解决客户的哪些痛点。研发人员往往只是拿到了解决方案,需要把它实现,但没有和业务侧一起去思考解决方案是否正确,能否真正的帮助客户解决问题。而用户影响地图通常是能够连接业务目标和产品功能的一种手段。 我们在每次迭代里加入的假设,也就是功能需求。首先把它先实现,再逐步去验证我们每一个小目标是否已经实现,再看下一个目标要是什么。那影响地图就是在这个过程中帮我们不断地去梳理目标和功能之间的关系。 我们在软件开发中可能存在的一些问题 针对这些问题,我们如何避免?先简单介绍做敏捷转型的常规思路: 先做团队级的敏捷,首先把产品、开发、测试人员,还有一些更后端的人员比如交互运维的同学放在一起,组成一个特训团队做交付。这个团队要包含交付过程中所涉及的所有角色。 接着业务敏捷要打通整个业务环节和研发侧的一个交付。上图中可以看到在敏捷中需求是分层管理的,第一层是业务需求,在这个层级是以用户目标和业务目标作为输入进行规划,同时需要去考虑客户的诉求。业务人员通过获取到的业务需求,进一步的和团队一起将其分解为产品需求。所以业务需求其实是我们真正去发布和运营的单元,它可以被独立发布到我们的生产环境上。我们的产品需求其实就是产品的具体功能,它是我们集成和测试的对象,也就是我们最终去部署到系统上的一个基本单元。产品需求再到了我们的开发团队,映射到迭代计划会上要把它分解为相应的技术任务,包括我们平时所说的比如一些前端的开发、后端的开发、测试都是相应的技术任务。所以业务敏捷要达到的目标是需要去持续顺畅高质量的交付业务价值。 将这几个点串起来,形成金字塔结构。最上层我们会把业务目标放在整个金字塔的塔尖。这个业务目标是通过用户的目标以及北极星指标确立的。确认业务目标后再去梳理相应的业务流程,最后生产。另外产品需求包含了操作流程和业务规则,具需求交付时间、工程时间以及我们的一些质量标准的要求。 谈到用户影响的地图,在敏捷江湖上其实有一个传说,大家都有一个说法叫做敏捷需求的“任督二脉”。用户影响地图其实就是任脉,在黑客马拉松上用过的用户故事地图其实叫督脉。所以说用户影响地图是在用户故事地图之前,先帮我们去梳理出我们要做哪些东西。当我们真正识别出我们要实现的业务活动之后,用户故事地图才去梳理我们整个的业务工作流,以及每个工作流节点下所要包含的具体功能和用户故事。所以说用户影响地图需要解决的问题,我们包括以下这些: 首先是范围蔓延,我们在整张地图上,功能和对应的业务目标是要去有一个映射的。这就避免了一些在我们比如有很多干系人参与的会议上,那大家都有不同想法些立场,会提出很多需求(正确以及错误的需求)。这个时候我们会依据目标去看这些需求是否真的是会影响我们的目标。 这里提到的错误需求,比如是利益相关的人提出的、客户认为产品应该有的、某个产品经理需求分析师认为可以有的....但是这些功能在用户影响地图中匹配不到对应目标的话,就需要降低优先级或弃掉。另外,通常我们去制定解决方案的时候,会考虑较完美的实现,导致解决方案括很多的功能。这个时候关键目标至关重要,会帮助我们梳理筛选、确定优先级。 看一下用户影响到地图概貌 总共分为一个三层的结构: 第一层why,你的业务目标哪个是最重要的,为什么?涉及到的角色有哪些? 第二层how ,怎样产生影响?影响用户角色什么样的行为? (不需要去列出所有的影响,基于业务目标) 第三层what,最关键的是在梳理需求时不需一次把所有细节想全,这通常团队中经常遇到的问题。 我们用这个例子来看一下 这是一个客服中心的影响地图,业务目标是 3个月内不增加客服人数的前提下能支持1.5倍的用户数。此业务目标设定是符合 smart 原则的,specific非常的具体,miserable 是可以衡量的,action reoriented是面向活动的, real list 也是很实际的。 量化的目标会指引我们接下来的行动,梳理一个业务目标,尽量去量化,比如 :我们通过打造一条什么样的流水线,能够提高整个部署的效率,时间是原来的 1/2 。这样才是一个能量化的有意义的目标。 回到这幅图, how 层级识别出来的内容,客服角色:想要对它施加的影响,把客户引导到论坛上,帮助客户更容易的跟踪问题,更快速的去定位问题。初级用户:方论坛上找到问题。高级用户:在论坛上回答问题。通过我们这些用户角色,进行活动,完成在不增加客户客服人数的前提下支持更多的用户数量。 最后一个层级,才是我们日常接触比较多的真正的功能的特性和需求,比如引导到客户到论坛上,其实这个产品就需要有一个常见问题的论坛的链接。这个层次需要我们团队进一步地在交付,在每个迭代之前做进一步的梳理,细化成相应的用户故事。 这个是云智慧团队中,自己做的影响地图的范例,可以看下整个的层级结构。序号表示优先级。 那我们用户影响地图可以总结为:
-
[访谈实录】一年半的前端 Bigo 1、2、3 访谈实录