第一次写面试经历,虽然之前有过一些电话面试经历,但相对而言感觉此次的经历对自己收获还是比较大,这里留下面经当作日记吧!(面试时间:2018-6-12 下午2:10;时长:50min;公司:
*)
1、说说React,为什么选择React
(一)、React特点
高效、虚拟DOM,最大限度地减少与DOM的交互:
- 浏览器在渲染网页时,会先将HTML文档解析并构建DOM树,然后与CSSOM树生成RenderObject树,最后渲染成页面。浏览器中渲染引擎和JavaScript引擎是分离的,渲染引擎会提供一些接口给JavaScript调用,它们二者通信是通过桥接的,性能其实是很差的。
- 以往,为了优化性能通常采用的办法是减少DOM操作次数。而React提出了一个新的思路就是虚拟DOM:组件的HTML结构不再是直接生成DOM,而是映射生成虚拟的JavaScript DOM结构,React通过diff算法将最小变更写入DOM中,从而减少DOM的实际次数,提升性能。
服务器端渲染
- React提供开箱即用的服务器端渲染,服务器端渲染解除了服务器端对浏览器的依赖,它会将“可视”部分先渲染,然后再交给客户端做渲染。
组件化编码
- React 的一切都是基于组件的。可以通过定义一个组件,然后在其他的组件中,可以像HTML标签一样引用它。说得通俗点,组件其实就是自定义的标签;通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
声明式设计
- React采用声明范式,函数式编程,可以轻松描述应用。
灵活
- React可以与已知的库或框架很好地配合。
JSX
- JSX 是 JavaScript 语法的扩展。React开发不一定使用 JSX ,但我们建议使用它。
单向响应的数据流
- React 实现了单向响应的数据流,数据是自顶向下单向流动的,即从父组件到子组件,这种原则让组件之间的关系变得简单可预测;props作为外部接口、state作为内部状态。
(二)、为什么选择React
- 相比其他如Vue、angular的双向数据绑定的框架,个人比较喜欢这种比较流程化的单向数据流形式,因为可以更好的预测数据的变化;
- 当然最主要的原因是学校图书馆,对于React的书籍资料更丰富(。。。。道出了真相、尴尬)
2、刚刚你说到了虚拟DOM的Diff算法,谈谈你对它的理解
- 因为当时没有怎么看这个算法,所以只是凭借之前的知识点简单的说了下;
- 这里在具体的回顾下:
- React的Diff算法巧妙的使用了试探法将复杂度为O(n^3)的数差异比较算法转换成O(n)复杂度的问题;
该算法基于两个假定:
- 相同类的两个组件将会生成相似的树形结构,而不同类的两个组件将会生成不同的树形结构;
- 可以为元素提供唯一的标识,确保该元素在不同的渲染过程中保持不变。
具体实现细节:
- (树形结构的比较)即首先检查两个节点的差异
节点类型不同时
- React会把它们当做两个不同的字树,导致直接移除之前的那颗子树,然后创建并插入之前的那颗子树
- 优点:巧妙的避开了对树形结构的大量差异检测,然后关注与相同的部分,实现了快速而又精确的差异检测逻辑;
- 缺陷:如果两个子树具有相似的结构,即顶节点不同,子节点结构相同,这时本来只需要对不同的顶节点进行删除插入操作,但是React会将整个子树都删除然后从新构建插入
3、对React生命周期了解吗
(1)、装载过程
当组件第一次被渲染时,依次调用的函数:
- construction
- getInitalState
- getDefaultProps
- componentWillMount
- render
- componentDidMount
(2)、更新过程
更新过程会依次调用以下生命周期函数,其中render函数和“装载”过程一样:
– componentWillReceiveProps
– shouldComponentUpdate- componentWillUpdate
- render
- componentDidUpdate
- 并不是所有的更新过程都会执行全部函数。
(3)、React组件的卸载过程只涉及一个函数componentWillUnmount
- 具体详情介绍可以看React生命周期详解
4、说到shouldComponentUpdate,你是如何使用的、如何判断是否更新
- 在生命周期中render函数决定了该渲染什么,而shouldComponentUpdate决定了一个组件什么时候不需要渲染;
- shouldComponentUpdate(nextProps,nextState)接收两个参数,即此次渲染的props和state对象,返回一个布尔,默认返回true,表示进行更新;返回false表示此次更新不需要到此终止;
- 通过比较此次props、state和上次是否相同决定是否需要进行更新可以很好的避免不必要的更新操作,提升性能;
- 因为props和state的是对象,平时使用的一般都浅层比较,如果需要对两个对象进行深度全面比较,我考虑的是使用JSON.stringify()
5、如果在React中我需要获取DOM节点、应该在什么阶段获取、如何获取
- 要获取DOM节点的话,需要在componentDidMount或者componentDidUpdata中获取,因为只有在这个阶段真实DOM节点才构建完成了
- 获取DOM节点可以使用ref属性
6、如果我又需要通过原生js获取这个节点的父节点呢
- 可能只是作为一个过渡,比较简单、直接回答了使用DOM节点的parentNode属性获取;不过这里还是补充一点。
nextSibling 属性 :
- 返回目标节点的下一个兄弟节点。
- 如果目标节点后面没有同属于一个父节点的节点,nextSibling 将返回null ;
- nextSibling 属性是一个只读属性。
previousSibling属性 :
- 返回目标节点的前一个兄弟节点。
- 如果目标节点前面没有同属于一个父节点的节点,previousSibling 将返回null ;
- previousSibling 属性是一个只读属性。
parentNode 属性 :
- 注:parentNode属性返回的节点永远是一个元素节点,因为只有元素节点才有可能有子节点。
- 当然有个例外:
- document节点,他没有父节点。所以document节点的parentNode属性将返回null;
- parentNode 属性是一个只读属性。
7、现在我又需要以类名获取DOM
- 当时直接说的是:可以使用document.getElementsByClassName()、document.querySelector()、document.querySelectorAll();
- 然后又引出了区别问题~
8、说说以上三种方法的区别
回答:
- getElementsByClassName()、和querySelectorAll()返回的是一个DOM列表,虽然可以用循环遍历,但是它们只是伪数组,不能使用数组的方法;
- getElementsByClassName()只能通过类名获取,而和querySelectorAll、querySelector是使用CSS选择器获取的,
- 当时只说了querySelector是返回一个DOM节点,有点错误吧;querySelector是返回文档中匹配指定的CSS选择器的第一元素
9、刚刚你说到伪数组,那么如何区分一个对象是数组呢
- 回答:
-
obj instanceof Array
如果返回true或者false; -
Object.prototype.toString.call(obj) === "[object Array]"
;
- 还有一个当时没有说道:
-
obj.constructor == Array
为true或false
- 当时还补充了
Array.isArray(obj)
,用于确定传递的值是否是一个 Array,如果对象是 Array,则为true; 否则为false。
10、谈到ES6,说说你对ES6的了解
- 回答:
- ES6主要增加了一些新的特效,如:
- const定义常量,但是const定义的常量只是值不可变,即基本数据类型不可变,对于引用类型,因为它建立的是引用,所以即使使用const定义的对象,其属性还是可变的;
- let定义变量,相对于var,它修复了一些问题,比如变量提升、重复定义等问题,并且const和let的定义具有块级作用域;
- 对于字符串扩展有:字符串模板、还有一些方法(忘了,赶紧跳过);
- 还有就是:剩余参数、函数默认参数、模块化、Promise、装饰器、Symbol、set和map等;
- 刚学习不久,大概就说了这些;最近也在更新ES6的笔记深入理解ES6
对ES6并没有深入的问,直接从let和const的块级作用域引到了闭包
11、刚刚说到块级作用域,谈谈它和闭包的区别,就是说一般什么时候使用闭包?
回答:
- 闭包使用的作用的话主要是为了获取函数内部的变量和将变量保存下来,使其不被垃圾回收器回收,供给之后使用;
- 使用的话一般是为了封装作用域,即代替全局变量,避免全局变量污染;
- 因为使用闭包,会将变量保存不被回收器回收,所以应该尽量避免使用,防止造成内存泄漏
- 答的不是太全面,这里补充:
闭包的应用场景
- 使用闭包代替全局变量
- 函数外或在其他函数中访问某一函数内部的参数
- 在函数执行之前为要执行的函数提供具体参数
- 在函数执行之前为函数提供只有在函数执行或引用时才能知道的具体参数
- 为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点
- 暂停执行
- 包装相关功能
12、说到回收器,说说如何判断一个变量可回收,如果我需要使用闭包,该如何避免你所说的内存泄漏?
- 感觉有点给自己挖坑了,(回收机制、内存泄漏不是太懂啊~~)
回答:
- 一个变量的可回收性,一般需要根据浏览器js引擎的回收器机制判断,大概有两种方式(当时只说到了一种);
- 即引用计数:引用计数的含义是跟踪记录每个变量被引用的次数,当期的引用次数为0时表回收
- 一般手动清除是将它赋值为null
科普:
引用计数:
- 当声明一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数便是1,如果同一个值又被赋给另一个变量,则该值的引用次数加1,相反,如果包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减1。当这个值的引用次数为0时,说明没有办法访问到它了,因而可以将其占用的内存空间回收
标记清除:
- 当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
13、HTML5了解吗?谈一谈
- HTML5 主要增加了一些语义化标签和一些API、删除了一些元素
- 绘画 canvas
- 用于媒介回放的 video 和 audio 元素
- 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失
- sessionStorage 的数据在浏览器关闭后自动删除
- 语意化更好的内容元素,比如 article、footer、header、nav、section
- 表单控件,calendar、date、time、email、url、search
- 新的技术webworker, websocket, Geolocation
- 移除的元素:
- 纯表现的元素:basefont,big,center,font, s,strike,tt,u
- 对可用性产生负面影响的元素:frame,frameset,noframes
没有到这么详细,以上回顾
14、说到localStorage和sessionStorage还有cookie谈谈他们的区别?
- 相同点:都可以作为浏览器存储,且都不能进行跨域访问;
不同点:
- cookie始终会在同源 http 请求头中携带(即使不需要),在浏览器和服务器间来回传递
- sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存;
- sessionStorage 和 localStorage 存储大小比cookie大得多,可以达到5M或更大;
- localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
- sessionStorage 数据在当前浏览器窗口关闭后自动删除;
- cookie 设置的cookie过期时间之前一直有效,与浏览器是否关闭无关。
15、简历上看你上过《计算机网络》?谈一谈ISO七层,按顺序说说
应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
16、说说http和ftp协议分别在哪一层
- 都处于应用层(现在回顾好像错了一个~~~~)
科普:
- 应用层:允许访问OSI环境的手段(应用协议数据单元APDU)(HTTP、FTP、SMTP、DNS)
- 表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU)
- 会话层:建立、管理和终止会话(会话协议数据单元SPDU);
- 传输层:提供端到端的可靠报文传递和错误恢复(段Segment)(TCP和UDP);
- 网络层:负责数据包从源到宿的传递和网际互连(包PackeT)IP寻址;
- 数据链路层:将比特组装成帧和点到点的传递(帧Frame)
- 物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)
17、说说你对http的了解,就说状态码吧,你常用的状态码及其含义
- HTTP超文本传输协议,基于请求/响应模式。
- HTTP是无状态协议,FTP是有状态
状态码:
- 100:post请求第一次发送头信息后服务器返回100,表示请求继续;
- 200: 表示请求正常,请求成功;
- 301:永久重定向;
- 302: 临时重定向;
- 304: 用于协商缓存,表示浏览器缓存资源未改变,任然可用
- 400:请求语法或参数错误;
- 401:认证未通过,如跨域未通过;
- 403:请求未授权;
- 404:找不到相应资源的位置;
- 500:服务器内部出错;
- 502: 错误网关
18、听你谈到浏览器缓存,说说你所知道的缓存方式
- 缓存类型有两种,强缓存和协商缓存
- 强缓存:不会向服务器发送请求,直接从缓存中读取资源(Expires、cache-control);
- 协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
科普更多浏览器缓存细节:
深入浏览器缓存
19、那接下来我们谈谈CSS,两个div垂直布局,上面的设置margin—bottom:50px;下面的margin-top:100px;此时两个div相距多少
- 100px,因为垂直方向的margin会存在重叠现象
- 深入了解可阅读CSS可格式化模型
20、如果我需要消除这种情况,如何处理
因为是消除两个兄弟div间的margin重叠问题:
- 两个兄弟div设置为float:left(或display:inline-block);width:100%;
科普更多(父子间的margin重叠消除)
- 外层元素padding代替
- 内层元素透明边框 border:1px solid transparent;
- 内层元素绝对定位 postion:absolute:
- 外层元素 overflow:hidden;
- 内层元素 加float:left;或display:inline-block;
- 内层元素padding:1px;
21、现在我需要实现两个div水平布局
- 使用flex布局:父元素设置display;flex
- 使用float布局(或者display:inline-block)
//html
<div class="box">
<div class="item1">float</div>
<div class="item2">overflow:hidden</div>
</div>
//CSS
.item1 {
float: left;
//display:inline-block
}
.item2 {
overflow: hidden;
//display:inline-block
}
22、现在我改变需求,左边固定宽度,右边自适应
- 左边定宽width:200px;设置浮动float:left;右边overflow:hidden(或者margin-left:左边宽度);
- 父元素display:flex;左边定宽width:200px;;右边设置项伸展属性flex-grow: 1;
- 父元素position:relative;左边定宽width:200px;position:absolute;右边margin-left:左边宽度;
- 父元素display:table;左边定宽width:200px;右边width:100%;display:tabel-cell;。
23、现在我需要,两个div进行两列布局,要求高度不定(父元素也是),我需要两个div实时等高,即左边div高度被其内子元素撑高时,右边的div高度和左边同步
- 父元素display:table;左边定宽width:200px;display:tabel-cell;右边width:100%。
- 父元素display:flex;align-items:stretch;左边定宽width:200px;;右边设置项伸展属性flex-grow: 1;
24、突然又有新的需求,实现左右两边固定宽度,中间自适应
- 使用flex布局:父元素display:flex;左边定宽width:200px;右边定宽width:200px;中间flex-grow:1;
使用浮动布局:父元素overflow:hidden;左边定宽width:200px;float:left;右边定宽width:200px;float:right;中间margin:0 200px;
- 这里有个坑,那就是HTML结构必须得变换,即中和右要调换位置(当时没想到,一直被问你确定就这样可以?短路短路~)
- 细想因为center的div在正常文档流中,且占据全屏宽度,自然之后的浮动元素会被放置下一行
// Html结构
<div class="box">
<div class="left">left</div>
<div class="right">right</div>
<div class="center">center</div>
</div>
// CSS样式布局
.left {
width: 200px;
background: bisque;
float: left;
}
.center{
background: red;
margin: 0 200px;
}
.right {
background: rgba(22,220,22,0.5);
float: right;
width: 200px;
}
使用绝对定位布局:
绝对定位法原理是将左右两边使用absolute定位,因为绝对定位使其脱离文档流,后面的center会自然流动到他们上面,然后使用margin属性,留出左右元素的宽度,既可以使中间元素自适应屏幕宽度。(这种方式)
// CSS样式布局 .left { width: 200px; background: bisque; position: absolute; } .center{ background: red; margin: 0 200px; } .right { background: rgba(22,220,22,0.5); position: absolute; right: 0; width: 200px; }
// html结构
<div class="box">
<div class="wrap">
<div class="center">center</div>
</div>
<div class="left">left</div>
<div class="right">right</div>
</div>
// CSS 样式
.left {
width: 200px;
background: bisque;
float: left;
margin-left: -100%;
}
.wrap {
float: left;
width: 100%;
}
.center{
background: red;
margin: 0 200px;
}
.right {
background: rgba(22,220,22,0.5);
float: left;
margin-left: -200px;
width: 200px;
}
整个过程历时1个小时左右,感觉还好吧,怎么说不管过没过,起码知道自己还有哪些方面不足,可以针对性的深入学习。
- 最后附上此次面试涉及的一些知识点
持续跟进中,喜欢可以留下个赞哦~~