合格前端系列第九弹-前端面试那些事

项目相关

  1. 自我介绍:职业经历,项目经历
  2. 选一个你以为印象最深的项目讲一讲,而后会从项目里面切入到 web 基础(html/css/js),这一块大概会聊 20-30 分钟,因此必定要提早选好一个本身作过的得意的项目,花一点时间捋一捋你以为项目中出色的点,用到了比较 hack,比较酷炫的方法解决了哪些痛点。

JS 基础(ES5)

  1. 原型:这里能够谈不少,只要围绕 [[ prototype ]] 谈,都没啥问题
  2. 闭包:牵扯做用域,能够二者联系起来一块儿谈
  3. 做用域:词法做用域,动态做用域
  4. this:不一样状况的调用,this 指向分别如何。顺带能够提一下 es6 中箭头函数没有 this, arguments, super 等,这些只依赖包含箭头函数最接近的函数
  5. call,apply,bind 三者用法和区别:参数、绑定规则(显示绑定和强绑定),运行效率(最终都会转换成一个一个的参数去运行)、运行状况(call,apply 当即执行,bind 是return 出一个 this “固定”的函数,这也是为何 bind 是强绑定的一个缘由)。

注:“固定”这个词的含义,它指的固定是指只要传进去了 context,则 bind 中 return 出来的函数 this 便一直指向 context,除非 context 是个变量 6. 变量声明提高:js 代码在运行前都会进行 AST 解析,函数申明默认会提到当前做用域最前面,变量申明也会进行提高。但赋值不会获得提高。关于 AST 解析,这里也能够说是造成词法做用域的主要缘由javascript

这里若是面试官问到2,3,4,5,6中的一点,你可以把2,3,4,5,6整理到一块儿,串联起来进行统一的回答效果极佳css

具体参考 从指向看JavaScripthtml

JS 基础(ES6)

  1. let,const:let 产生块级做用域(一般配合 for 循环或者 {} 进行使用产生块级做用域),const 申明的变量是常量(内存地址不变)
  2. Promise:这里你谈 promise 的时候,除了将他解决的痛点以及经常使用的 API 以外,最好进行拓展把 eventloop 带进来好好讲一下,microtask、macrotask 的执行顺序,若是看过 promise 源码,最好能够谈一谈 原生 Promise 是如何实现的。Promise 的关键点在于callback 的两个参数,一个是 resovle,一个是 reject。还有就是 Promise 的链式调用(Promise.then(),每个 then 都是一个责任人)。

详细参考 my-promisevue

  1. Generator:遍历器对象生成函数,最大的特色是能够交出函数的执行权
  • function 关键字与函数名之间有一个星号;
  • 函数体内部使用 yield 表达式,定义不一样的内部状态;
  • next 指针移向下一个状态

这里你能够说说 Generator 的异步编程,以及它的语法糖 asyncawiat,传统的异步编程。ES6 以前,异步编程大体以下java

  • 回调函数
  • 事件监听
  • 发布/订阅

传统异步编程方案之一:协程,多个线程互相协做,完成异步任务。node

  1. async、await:Generator 函数的语法糖。有更好的语义、更好的适用性、返回值是 Promise。
  • async => *
  • await => yield

基本用法react

async function timeout (ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms)    
  })
}
async function asyncConsole (value, ms) {
  await timeout(ms)
  console.log(value)
}
asyncConsole('hello async and await', 1000)

注:最好把2,3,4 连到一块儿讲webpack

  1. AMD,CMD,CommonJs,ES6 Module:解决原始无模块化的痛点
  • AMD:requirejs 在推广过程当中对模块定义的规范化产出,提早执行,推崇依赖前置
  • CMD:seajs 在推广过程当中对模块定义的规范化产出,延迟执行,推崇依赖就近
  • CommonJs:模块输出的是一个值的 copy,运行时加载,加载的是一个对象(module.exports 属性),该对象只有在脚本运行完才会生成
  • ES6 Module:模块输出的是一个值的引用,编译时输出接口,ES6 模块不是对象,它对外接口只是一种静态定义,在代码静态解析阶段就会生成。

CSS相关

  1. 左边定宽,右边自适应方案:float + margin,float + calc
/* 方案1 */
.left {
  width: 120px;
  float: left;
}
.right {
  margin-left: 120px;
}
/* 方案2 */
.left {
  width: 120px;
  float: left;
}
.right {
  width: calc(100% - 120px);
  float: left;
}
  1. 左右两边定宽,中间自适应:float,float + calc, 圣杯布局(设置BFC,margin负值法),flex
.wrap {
  width: 100%;
  height: 200px;
}
.wrap > div {
  height: 100%;
}
/* 方案1 */
.left {
  width: 120px;
  float: left;
}
.right {
  float: right;
  width: 120px;
}
.center {
  margin: 0 120px; 
}
/* 方案2 */
.left {
  width: 120px;
  float: left;
}
.right {
  float: right;
  width: 120px;
}
.center {
  width: calc(100% - 240px);
  margin-left: 120px;
}
/* 方案3 */
.wrap {
  display: flex;
}
.left {
  width: 120px;
}
.right {
  width: 120px;
}
.center {
  flex: 1;
}
  1. 左右居中
  • 行内元素: text-align: center
  • 定宽块状元素: 左右 margin 值为 auto
  • 不定宽块状元素: table布局,position + transform
/* 方案1 */
.wrap {
  text-align: center
}
.center {
  display: inline;
  /* or */
  /* display: inline-block; */
}
/* 方案2 */
.center {
  width: 100px;
  margin: 0 auto;
}
/* 方案2 */
.wrap {
  position: relative;
}
.center {
  position: absulote;
  left: 50%;
  transform: translateX(-50%);
}
  1. 上下垂直居中:
  • 定高:margin,position + margin(负值)
  • 不定高:position + transform,flex,IFC + vertical-align:middle
/* 定高方案1 */
.center {
  height: 100px;
  margin: 50px 0;   
}
/* 定高方案2 */
.center {
  height: 100px;
  position: absolute;
  top: 50%;
  margin-top: -25px;
}
/* 不定高方案1 */
.center {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
/* 不定高方案2 */
.wrap {
  display: flex;
  align-items: center;
}
.center {
  width: 100%;
}
/* 不定高方案3 */
/* 设置 inline-block 则会在外层产生 IFC,高度设为 100% 撑开 wrap 的高度 */
.wrap::before {
  content: '';
  height: 100%;
  display: inline-block;
  vertical-align: middle;
}
.wrap {
  text-align: center;
}
.center {
  display: inline-block;  
  vertical-align: middle;
}
  1. 盒模型:content(元素内容) + padding(内边距) + border(边框) + margin(外边距)

延伸: box-sizingcss3

  • content-box:默认值,总宽度 = margin + border + padding + width
  • border-box:盒子宽度包含 padding 和 border,总宽度 = margin + width
  • inherit:从父元素继承 box-sizing 属性
  1. BFC、IFC、GFC、FFC:FC(Formatting Contexts),格式化上下文
  • BFC:块级格式化上下文,容器里面的子元素不会在布局上影响到外面的元素,反之也是如此(按照这个理念来想,只要脱离文档流,确定就能产生 BFC)。产生 BFC 方式以下

float 的值不为 none。nginx

overflow 的值不为 visible。

position 的值不为 relative 和 static。

display 的值为 table-cell, table-caption, inline-block中的任何一个。

用处?常见的多栏布局,结合块级别元素浮动,里面的元素则是在一个相对隔离的环境里运行。

  • IFC:内联格式化上下文,IFC 的 line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的 padding/margin 影响)。

IFC中的line box通常左右都贴紧整个 IFC,可是会由于 float 元素而扰乱。float 元素会位于 IFC 与 line box 之间,使得 line box 宽度缩短。 同个 ifc 下的多个 line box 高度会不一样。 IFC 中时不可能有块级元素的,当插入块级元素时(如 p 中插入 div )会产生两个匿名块与 div 分隔开,即产生两个 IFC ,每一个 IFC 对外表现为块级元素,与 div 垂直排列。

用处?

水平居中:当一个块要在环境中水平居中时,设置其为 inline-block 则会在外层产生IFC,经过 text-align 则可使其水平居中。

垂直居中:建立一个 IFC,用其中一个元素撑开父元素的高度,而后设置其 vertical-align: middle,其余行内元素则能够在此父元素下垂直居中

  • GFC:网格布局格式化上下文(display: grid)
  • FFC:自适应格式化上下文(display: flex)

详细参考 css3中的BFC,IFC,GFC和FFC

框架相关

  1. 数据双向绑定原理:常见数据绑定的方案
  • Object.defineProperty(vue):劫持数据的 getter 和 setter
  • 脏值检测(angularjs):经过特定事件进行轮循
  • 发布/订阅模式:经过消息发布并将消息进行订阅

详细细节参考 实现一个属于咱们本身的简易MVVM库

扩充:如何监听数组变化

  1. VDOM:三个 part,
  • 虚拟节点类,将真实 DOM 节点用 js 对象的形式进行展现,并提供 render 方法,将虚拟节点渲染成真实 DOM
  • 节点 diff 比较:对虚拟节点进行 js 层面的计算,并将不一样的操做都记录到 patch 对象
  • re-render:解析 patch 对象,进行 re-render

详细请参考 实现Virtual Dom && Diff

补充1:VDOM 的必要性?

  • 建立真实DOM的代价高:真实的 DOM 节点 node 实现的属性不少,而 vnode 仅仅实现一些必要的属性,相比起来,建立一个 vnode 的成本比较低。

  • 触发屡次浏览器重绘及回流:使用 vnode ,至关于加了一个缓冲,让一次数据变更所带来的全部 node 变化,先在 vnode 中进行修改,而后 diff 以后对全部产生差别的节点集中一次对 DOM tree 进行修改,以减小浏览器的重绘及回流。

补充2:vue 为何采用 vdom?

引入 Virtual DOM 在性能方面的考量仅仅是一方面。

性能受场景的影响是很是大的,不一样的场景可能形成不一样实现方案之间成倍的性能差距,因此依赖细粒度绑定及 Virtual DOM 哪一个的性能更好还真不是一个容易下定论的问题。

Vue 之因此引入了 Virtual DOM,更重要的缘由是为了解耦 HTML 依赖,这带来两个很是重要的好处是:

  • 再也不依赖 HTML 解析器进行模版解析,能够进行更多的 AOT 工做提升运行时效率:经过模版 AOT 编译,Vue 的运行时体积能够进一步压缩,运行时效率能够进一步提高;
  • 能够渲染到 DOM 之外的平台,实现 SSR、同构渲染这些高级特性,Weex 等框架应用的就是这一特性。

综上,Virtual DOM 在性能上的收益并非最主要的,更重要的是它使得 Vue 具有了现代框架应有的高级特性。

  1. vue 和 react 区别
  • 相同点:都支持 ssr,都有 vdom,组件化开发,实现 webComponents 规范,数据驱动等
  • 不一样点:vue 是双向数据流(固然为了实现单数据流方便管理组件状态,vuex 便出现了),react 是单向数据流。vue 的 vdom 是追踪每一个组件的依赖关系,不会渲染整个组件树,react 每当应该状态被改变时,所有子组件都会 re-render。

上面提到的每一个点,具体细节还得看本身的理解

  1. 为何用 vue :简洁、轻快、舒服、没了

网络基础类

  1. 跨域:不少种方法,但万变不离其宗,都是为了搞定同源策略。重用的有 jsonp、iframe、cors、img、HTML5 postMessage等等。其中用到 html 标签进行跨域的原理就是 html 不受同源策略影响。但只是接受 Get 的请求方式,这个得清楚。

详细内容传送门

延伸1:img iframe script 来发送跨域请求有什么优缺点?

  • iframe

优势:跨域完毕以后DOM操做和互相之间的JavaScript调用都是没有问题的

缺点:1.若结果要以URL参数传递,这就意味着在结果数据量很大的时候须要分割传递,巨烦。2.还有一个是iframe自己带来的,母页面和iframe自己的交互自己就有安全性限制。

  • script

优势:能够直接返回json格式的数据,方便处理

缺点:只接受GET请求方式

  • 图片ping

优势:能够访问任何url,通常用来进行点击追踪,作页面分析经常使用的方法

缺点:不能访问响应文本,只能监听是否响应

延伸2:配合 webpack 进行反向代理?

webpack 在 devServer 选项里面提供了一个 proxy 的参数供开发人员进行反向代理

'/api': {
  target: 'http://www.example.com', // your target host
  changeOrigin: true, // needed for virtual hosted sites
  pathRewrite: {
    '^/api': ''  // rewrite path
  }
},

而后再配合 http-proxy-middleware 插件对 api 请求地址进行代理

const express = require('express');
const proxy = require('http-proxy-middleware');
// proxy api requests
const exampleProxy = proxy(options); // 这里的 options 就是 webpack 里面的 proxy 选项对应的每一个选项

// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);

而后再用 nginx 把容许跨域的源地址添加到报头里面便可

说到 nginx ,能够再谈谈 CORS 配置,大体以下

location / {
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';  
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; 
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Headers' 'DNT, X-Mx-ReqToken, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type';  
    add_header 'Access-Control-Max-Age' 86400;  
    add_header 'Content-Type' 'text/plain charset=UTF-8';  
    add_header 'Content-Length' 0;  
    return 200;  
  }
}
  1. http 无状态无链接
  • http 协议对于事务处理没有记忆能力
  • 对同一个url请求没有上下文关系
  • 每次的请求都是独立的,它的执行状况和结果与前面的请求和以后的请求是无直接关系的,它不会受前面的请求应答状况直接影响,也不会直接影响后面的请求应答状况
  • 服务器中没有保存客户端的状态,客户端必须每次带上本身的状态去请求服务器
  • 人生若只如初见,请求过的资源下一次会继续进行请求

http协议无状态中的 状态 到底指的是什么?!

【状态】的含义就是:客户端和服务器在某次会话中产生的数据

那么对应的【无状态】就意味着:这些数据不会被保留

but

  • 经过增长cookie和session机制,如今的网络请求实际上是有状态的
  • 在没有状态的http协议下,服务器也必定会保留你每次网络请求对数据的修改,但这跟保留每次访问的数据是不同的,保留的只是会话产生的结果,而没有保留会话
  1. http-cache:就是 http 缓存咯

首先得明确 http 缓存的好处

  • 减小了冗余的数据传输,减小网费
  • 减小服务器端的压力
  • Web 缓存可以减小延迟与网络阻塞,进而减小显示某个资源所用的时间
  • 加快客户端加载网页的速度

常见 http 缓存的类型

  • 私有缓存(通常为本地浏览器缓存)
  • 代理缓存

而后谈谈本地缓存

本地缓存是指浏览器请求资源时命中了浏览器本地的缓存资源,浏览器并不会发送真正的请求给服务器了。它的执行过程是:

  • 第一次浏览器发送请求给服务器时,此时浏览器尚未本地缓存副本,服务器返回资源给浏览器,响应码是200 OK,浏览器收到资源后,把资源和对应的响应头一块儿缓存下来。
  • 第二次浏览器准备发送请求给服务器时候,浏览器会先检查上一次服务端返回的响应头信息中的Cache-Control,它的值是一个相对值,单位为秒,表示资源在客户端缓存的最大有效期,过时时间为第一次请求的时间减去Cache-Control的值,过时时间跟当前的请求时间比较,若是本地缓存资源没过时,那么命中缓存,再也不请求服务器。
  • 若是没有命中,浏览器就会把请求发送给服务器,进入缓存协商阶段。

与本地缓存相关的头有:Cache-Control、Expires,Cache-Control有多个可选值表明不一样的意义,而Expires就是一个日期格式的绝对值。

Cache-Control

Cache-Control是HTPP缓存策略中最重要的头,它是HTTP/1.1中出现的,它由以下几个值

  • no-cache:不使用本地缓存。须要使用缓存协商,先与服务器确认返回的响应是否被更改,若是以前的响应中存在ETag,那么请求的时候会与服务端验证,若是资源未被更改,则能够避免从新下载。
  • no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
  • public:能够被全部的用户缓存,包括终端用户和CDN等中间代理服务器。
  • private:只能被终端用户的浏览器缓存,不容许CDN等中继缓存服务器对其缓存。
  • max-age:从当前请求开始,容许获取的响应被重用的最长时间(秒)。

例如:

Cache-Control: public, max-age=1000

表示资源能够被全部用户以及代理服务器缓存,最长时间为1000秒。

Expires

Expires是HTTP/1.0出现的头信息,一样是用于决定本地缓存策略的头,它是一个绝对时间,时间格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要发送请求时间是在Expires以前,那么本地缓存始终有效,不然就会去服务器发送请求获取新的资源。若是同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。他们能够这样组合使用

Cache-Control: public
Expires: Wed, Jan 10 2018 00:27:04 GMT

所谓的缓存协商

当第一次请求时服务器返回的响应头中存在如下状况时

  • 没有Cache-Control和Expires
  • Cache-Control和Expires过时了
  • Cache-Control的属性设置为no-cache时

那么浏览器第二次请求时就会与服务器进行协商,询问浏览器中的缓存资源是否是旧版本,需不须要更新,此时,服务器就会作出判断,若是缓存和服务端资源的最新版本是一致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码,若是服务器发现浏览器中的缓存已是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200 Ok,那么服务端是根据什么来判断浏览器的缓存是否是最新的呢?实际上是根据HTTP的另外两组头信息,分别是:Last-Modified/If-Modified-Since 与 ETag/If-None-Match

Last-Modified 与 If-Modified-Since

  • 浏览器第一次请求资源时,服务器会把资源的最新修改时间Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT放在响应头中返回给浏览器
  • 第二次请求时,浏览器就会把上一次服务器返回的修改时间放在请求头If-Modified-Since:Thu, 29 Dec 2011 18:23:55发送给服务器,服务器就会拿这个时间跟服务器上的资源的最新修改时间进行对比

若是二者相等或者大于服务器上的最新修改时间,那么表示浏览器的缓存是有效的,此时缓存会命中,服务器就再也不返回内容给浏览器了,同时Last-Modified头也不会返回,由于资源没被修改,返回了也没什么意义。若是没命中缓存则最新修改的资源连同Last-Modified头一块儿返回。

第一次请求返回的响应头:

Cache-Control:max-age=3600
Expires: Fri, Jan 12 2018 00:27:04 GMT
Last-Modified: Wed, Jan 10 2018 00:27:04 GMT

第二次请求的请求头信息:

If-Modified-Since: Wed, Jan 10 2018 00:27:04 GMT

这组头信息是基于资源的修改时间来判断资源有没有更新,另外一种方式就是根据资源的内容来判断,就是接下来要讨论的ETag与If-None-Match

ETag与If-None-Match

ETag/If-None-Match与Last-Modified/If-Modified-Since的流程实际上是相似的,惟一的区别是它基于资源的内容的摘要信息(好比MD5 hash)来判断

浏览器发送第二次请求时,会把第一次的响应头信息ETag的值放在If-None-Match的请求头中发送到服务器,与最新的资源的摘要信息对比,若是相等,取浏览器缓存,不然内容有更新,最新的资源连同最新的摘要信息返回。用ETag的好处是若是由于某种缘由到时资源的修改时间没改变,那么用ETag就能区分资源是否是有被更新。

第一次请求返回的响应头:

Cache-Control: public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"

第二次请求的请求头信息:

If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
  1. cookie 和 session
  • session: 是一个抽象概念,开发者为了实现中断和继续等操做,将 user agent 和 server 之间一对一的交互,抽象为“会话”,进而衍生出“会话状态”,也就是 session 的概念
  • cookie:它是一个世纪存在的东西,http 协议中定义在 header 中的字段,能够认为是 session 的一种后端无状态实现

如今咱们常说的 “session”,是为了绕开 cookie 的各类限制,一般借助 cookie 自己和后端存储实现的,一种更高级的会话状态实现

session 的常见实现要借助cookie来发送 sessionID

  1. 安全问题,如 XSS 和 CSRF
  • XSS:跨站脚本攻击,是一种网站应用程序的安全漏洞攻击,是代码注入的一种。常见方式是将恶意代码注入合法代码里隐藏起来,再诱发恶意代码,从而进行各类各样的非法活动。

防范:记住一点 “全部用户输入都是不可信的”,因此得作输入过滤和转义

详细点击 对于跨站脚本攻击(XSS攻击)的理解和总结

  • CSRF:跨站请求伪造,也称 XSRF,是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。与 XSS 相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。

防范:用户操做验证(验证码),额外验证机制(token使用)等

详细点击 对于跨站伪造请求(CSRF)的理解和总结

相关文章
相关标签/搜索