前端面试问题总结
HTTP相关:
- HTTP和HTTPS
HTTP协议通常承载于TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS。
默认HTTP的端口号为80,HTTPS的端口号为443。
- 为什么HTTPS安全
因为网络请求需要中间有很多的服务器路由器的转发。中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有。
https之所以比http安全,是因为他利用ssl/tls协议传输。它包含证书,卸载,流量转发,负载均衡,页面适配,浏览器适配,refer传递等。保障了传输过程的安全性
-
有几种方式可以实现存储功能,分别有什么优缺点?
- cookie已不适合用于存储,需注意安全问题
- localstorage用于不怎么改变的数据,否则用sessionstorage
- indexDB适用客户端存储大量的结构化数据,并且使用索引高效检索
-
从浏览器地址栏输入url到显示页面的步骤
- 浏览器根据请求的
URL
交给DNS
域名解析,找到真实IP
,向服务器发起请求; - 服务器交给后台处理完成后返回数据,浏览器接收文件(
HTML、JS、CSS
、图象等); - 浏览器对加载到的资源(
HTML、JS、CSS
等)进行语法解析,建立相应的内部数据结构(如HTML
的DOM
); - 载入解析到的资源文件,渲染页面,完成。
- 浏览器渲染过程
- 将html分析成DOM树
- 把css文件解析为CSS Rule Tree (css应选择器避免过于具体 , 层级扁平 , 减少无意义标签 , 因为css渲染会递归CSSOM树)
- 将DOM和CSSOM整合生成渲染树(Rendering Tree)
- 根据渲染树进行布局(回流) , 将需要显示的节点显示在页面 (遇到script标签会阻塞)
- 加载一个资源的过程
1. 浏览器根据DNS服务器获取域名的IP地址
2. 向这个IP的服务器发送http请求
3. 服务器收到,处理,并返回http请求
4. 浏览器得到返回内容
- 什么是DOCTYPE及作用?
<!DOCTYPE html>
DTD: 声明文档类型 , 决定用哪种协议来解析
DOCTYPE: 告诉浏览器当前文档包含的是哪个DTD
- window.onload和DOMContentLoaded有何区别?
window.onload是页面全部资源(包括图片, 视频)加载完才会执行
DOMContentLoaded是DOM渲染完即可执行
- 插入几万个dom,如何实现不卡顿?
- 重绘与回流
- 重绘(Repaint)是当节点需要更改外观而不会影响布局的,比如改变
color
就叫称为重绘 - 回流(Reflow)是布局或者几何属性需要改变就称为回流。
- 减少 重绘与回流
- 使用
transform
替代top
- 使用
visibility
替换display: none
- 不要把节点的属性值放在一个循环里当成循环里的变量
- 不要使用
table
布局,可能很小的一个小改动会造成整个table
的重新布局 -
动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame
-
CSS 选择符从右往左匹配查找,避免节点层级过多
-
将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于
video
标签来说,浏览器会自动将该节点变为图层;以下常用属性可以生成新图层: will-change
video
、iframe
标签
- 如何进行性能优化?
- 多使用内存 , 缓存 , 减少cpu计算
强缓存表示在缓存期间,不需要请求,State Code为200,Cache-Control可以组合使用多个 , 强缓存可以通过设置Expires Cache-Control来实现 协商缓存: 协商缓存表示如果缓存过期了,那么就需要重新发起请求验证资源是否有更新,可通过设置HTTP Header的Last-Modified和ETag来实现,如果资源没有改变,State Code为304
- 资源压缩 ,合并, 减少http请求
- 非核心代码异步加载
//异步加载的方式 1: 动态脚本加载 2: defer 3: async
//异步加载的区别
defer: 在html解析完成执行 , 如果是多个 , 则按照加载顺序执行
async: 加载完成后立即执行 , 如果是多个 , 执行顺序与加载顺序无关 - 使用CDN
- DNS预解析:
DNS 解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的 IP。
<meta http-equiv="x-dns-prefetch-control" content="on"> //在https中打开a标签预解析
<link rel="dns-prefetch" href="//baidu.com">
图片:
- 修饰类图片用css
- 移动端图片尽量使用CDN加载(可以计算出屏幕适配宽度,请求相应裁切好的图片)
- 使用雪碧图
- 小图使用base64格式
- 选择正确的图片格式
- 照片使用jpeg
- 小图 , 图标 ,背景类使用png或者svg
- 动图gif
CSS:
css放在head里 (先加载css 然后渲染 否则渲染两次)
不使用css表达式
正确使用选择器 (应从右向左匹配 , 避免层级过多 , 扁平化)
css和js应从外部引入
JS:
JS放在body最下面 (不会阻塞渲染过程 性能优化问题; 再执行JS script能拿到所有标签)
减少DOM操作 , 对DOM查询进行缓存
多个操作尽量合并在一起执行
尽早在dom渲染完执行
删除不需要的脚本
节流:
隔一段时间发送请求:如在用户输入时,用户输入过快会导致一直发送请求,这时候我们可以使用节流函数,使隔一小段时间发送请求.
const throttle = (func, wait = 50) => { let lastTime = 0 return function (...args) { let now = +new Date() if (now - lastTime > wait) { lastTime = now func.apply(this, args) } } } window.addEventListener('scroll', throttle(() => { console.log(1) }, 500))
防抖:
有一个按钮点击会触发网络请求,但是我们并不希望每次点击都发起网络请求,而是当用户点击按钮一段时间后没有再次点击的情况才去发起网络请求,对于这种情况我们就可以使用防抖。
const debounce = (fn, delay = 50) => { let timer = 0; return function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args) }, delay) } } var btn = document.getElementById('input'); btn.addEventListener('click', debounce(function () { console.log('发送ajax请求') }), 1000)
预加载:
优点:降低首屏加载时间
缺点:兼容性不好
// 有些资源需要马上使用,可以使用如下代码实现,预加载不会阻塞onload事件
<link rel="preload" href="http://example.com">
预渲染:
// 将用户大概率会打开的资源进行预渲染,可以提升网页的加载速度
<link rel="prerender" href="http://example.com">
懒加载:
懒加载就是将不关键的资源延后加载。
对于图片来说,先设置图片标签的 src
属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src
属性,这样图片就会去下载资源,实现了图片懒加载。
懒加载不仅可以用于图片,也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。
var myImage = (function(){ var imgNode = document.createElement('img'); document.body.appendChild(imgNode); var img = new Image(); img.onload = function(){ imgNode.src = img.src; } return { setSrc: function(src){ imgNode.src = 'loading.gif'; img.src = src; } } })() myImage.setSrc("https://img1.sycdn.imooc.com/5c09123400014ba418720632.jpg");
- contenttype有哪几种类型?
application/json
text/xml
multipart/form-data
application/x-www-form-urlencoded
- HTTP协议的主要特点?
简单快速 (每个资源都是固定的URI)
灵活 (通过一个HTTP协议可以完成不同数据类型的传输)
无连接 (连接一次断掉 , 不会一直连接) ,
无状态 (客服端和服务端是两种身份,一次连接后就断开,下次再连接,服务端无法区分两次是否由同一客户端发起的请求)
- HTTP报文的组成部分:
由请求报文和响应报文组成
1. 请求报文:请求行、请求头、空行、请求体
2. 响应报文:状态行,响应头、空行、响应体
请求行包涵:http方法 、页面地址、http协议以及版本 (GET / HTTP1.1)
请求头:就是一些key\value值 , 告诉服务端我要什么内容 , 和要注意什么 类型
空行:遇到空行就能知道 下面不是请求头的部分了
请求体:数据
状态行: HTTP协议、状态码 (HTTP1.1 / 200 ok)
其它都是大同小异的
- HTTP方法有哪些?
GET---------获取资源
POST-------传输资源
PUT---------更新资源
DELETE------删除资源
HEAD--------获取报文首部
- Post请求和Get请求的区别是什么?主要作用在哪?
- get点击浏览器回退按钮不会再次提交 , post会.
- get请求能缓存 , post需要手动设置才可以
- get的参数可以保留在浏览器历史记录 , post不会
- get参数通过url传递 ,它是明文显示的 , 不能用来传敏感信息 , post通过request Body
- get传送参数长度是有限制的(2kb) , post无限制
- readyState状态码
0 - 未初始化 , 还没有调用send方法
1 - 载入 , 已调用send方法,正在发送请求
2 - 载入完成 , send()方法执行完成 , 已接收到全部响应内容
3 - 交互 , 正在解析响应内容
4 - 完成 , 响应内容解析完成 , 可以在客户端调用
- status状态码
1xx 指示信息-表示请求一接收,继续处理
2xx 表示成功处理请求 200表示请求正确处理 206表示范围请求
3xx 重定向 304表示使用缓存
4xx 客户端请求错误 请求有语法错误或请求无法实现(400语法错误,401请求未经授权,403请求页面被禁止,404请求资源不存在)
5xx 服务器端错误 服务器未能实现合法请求(500服务器错误,503服务器过载或宕机)
- 什么是持久链接?
http协议是支持持久连接的。必须是http1.1版本
持久连接 Keep-alive (http1.1版本才支持)
非Keep-ailve模式时,每个请求/应答客户端和服务器都要新建一个连接,完成后就断开
当Keep-ailve模式(又称持久连接、连接重用)时,客户端到服务器端的连接持续有效,避免后续请求时,重新建立连接
- 什么是管线化?
HTTP协议类:管线化(HTTP/1.1) GET和HEAD可以管线化 POST有所限制
持久连接:请求1 -》响应1-》请求2-》响应2-》请求3 -》响应3
管线化:请求1-》请求2-》请求3-》响应1-》响应2-》响应3 (把现在的请求一次打包传输过去,响应也是一次性返回过来,并且是在持久连接的情况 下完成的)
- 什么是同源策略及限制?跨域方式都有哪些?
同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议
限制:不是一个源的不能操作
如果协议、域名或者端口有一个不同就是跨域,Ajax 请求会失败。 (防止CSRF利用用户登录状态发起攻击)
JSONP (只能get请求) 原理是利用script
标签没有跨域限制,通过src
指向一个ajax
的URL,最后跟一个回调函数callback
// 实现自己的JSONP var jsonp = function(url , data , callback){ var cbName = 'callback_' + new Date().getTime(); var queryString = url.indexOf('?') == -1 ? '?' : '&'; for (var k in data){ queryString += k + "=" + data[k] + '&'; } queryString +='callback=' + cbName; var script = document.createElement('script'); script.src = url +queryString; window[cbName] = function(data){ callback(data) document.body.removeChild(script); }; document.body.appendChild(script); } // 实测 jsonp('http://api.douban.com/v2/movie/in_theaters',{'count':1},function(data){ console.log(data) })
CORS 关键是后端 , 服务端设置(Access-Control-Allow-Origin)
postmessage (用于获取嵌入页面中的第三方页面数据)
// 发送消息端 window.parent.postMessage('message', 'http://test.com') // 接收消息端 var mc = new MessageChannel() mc.addEventListener('message', event => { var origin = event.origin || event.originalEvent.origin if (origin === 'http://test.com') { console.log('验证通过') } })
document.domain (只需要给页面添加 document.domain = 'test.com'
表示二级域名都相同就可以实现跨域)
- Ajax具体怎么实现?
var xmr=new XMLHttpRequest();
xmr.open('GET','url',false);
xmr.onreadystatechange=function(){
if(xmr.readyState==4){
if((xmr.status>=200 && xmr.status<300)||xmr.status===304){
alert(xmr.responseText)
}
}
}
xmr.send(null)
- 什么是 XSS 攻击?如何防范 XSS 攻击?
基本概念:跨域脚本攻击
XSS 简单点来说,就是攻击者将可以执行的代码注入到网页中。
防范:转义输入输出的内容,对于引号、尖括号、斜杠进行转义
- 什么是 CSRF 攻击?如何防范 CSRF 攻击?
CSRF , 通常称为跨站请求伪造
原理:
在B网站引诱用户访问A网站(用户之前已经登陆过A网站,浏览器cookie缓存了身份验证信息),然后调用A网站的接口攻击A网站
举个例子,假设网站中有一个通过 GET
请求提交用户评论的接口,那么攻击者就可以在钓鱼网站中加入一个图片,图片的地址就是评论接口
防御措施:
(1)token验证:登陆成功后服务器下发token令牌存到用户本地,再次访问时要主动发送token,浏览器只能主动发cookie,做不到主动发token
(2)referer验证:判断页面来源是否自己站点的页面,不是不执行请求
(3)隐藏令牌: 令牌放在http header头中,而不是链接中 (和token类似)
- CSRF与xss的区别:
csrf需要用户登陆,利用网站自己的接口漏洞进行攻击
xss通过注入脚本执行自己的代码
HTML相关:
CSS相关:
- 如何清除浮动?
不清除浮动会发生高度塌陷:浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮动后,父元素会发生高度塌陷)
- 添加空div , 在浮动元素下方添加空div,并给该元素写css样式: {clear:both;height:0;overflow:hidden;}
- 父级设置高度
- 父级同时浮动(需要给父级同级元素添加浮动)
- 给父级添加overflow:hidden 清除浮动方法
- after伪类 (现在主流方法,推荐使用)
.float_div:after{ content:"."; clear:both; display:block; height:0; overflow:hidden; visibility:hidden; } .float_div{ zoom:1 }
- css如何实现三角形?
span { border-top: 40px solid transparent; border-left: 40px solid transparent; border-right: 40px solid transparent; border-bottom: 40px solid #ff0000; }
- css3如何实现0.5px的细线?
/* css */ .line { position: relative; } .line:after { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 1px; background-color: #000000; -webkit-transform: scaleY(.5); transform: scaleY(.5); } /* html */ <div class="line"></div>
- 如何实现div水平垂直居中?
宽高已知
.box { width: 400px; height: 200px; position: relative; background: red; } .content { width: 200px; height: 100px; position: absolute; top: 50%; left: 50%; margin-left: -100px; margin-top: -50px; background: green; }
宽高未知:
.box { width: 400px; height: 200px; position: relative; background: red; } .content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: green; }
flex:
.box { width: 400px; height: 200px; background: red; display: flex; justify-content: center; align-items: center; } .content { width: 200px; height: 100px; background: green; }
- 如何用css实现一行文字后面...的情况?
- 中间自适应解决方案
浮动:
缺点:脱离文档流,需清除浮动。
优点:兼容性好。
<style>
.box1 div{
min-height: 50px;
}
.box1 .left{
float: left;
width: 100px;
background: red;
}
.box1 .right{
float: right;
width: 100px;
background: green;
}
.box1 .center{
background: yellow;
}
</style>
<section class="box1">
<div class="left"></div>
<div class="right"></div>
<div class="center">浮动自适应部分</div>
</section>
绝对定位:
缺点:因为其本身脱离了文档流,导致了其子元素都脱离了文档流,使用性较差。
优点:比较快捷。
<style>
.box2{
min-height: 50px;
}
.box2 div{
position: absolute;
min-height: 50px;
}
.box2 .left{
width: 100px;
left: 0;
background: red;
}
.box2 .center{
left: 100px;
right: 100px;
background: yellow;
}
.box2 .right{
right: 0;
width: 100px;
background: green;
}
</style>
<section class="box2">
<div class="left"> </div>
<div class="center">绝对定位自适应部分</div>
<div class="right"></div>
</section>
flex:
缺点:只兼容到ie9。
优点:目前是比较完美的方案。
<style>
.box3{
display: flex;
min-height: 50px;
}
.box3 .left{
width: 100px;
background: red;
}
.box3 .center{
flex: 1;
background: yellow;
}
.box3 .right{
width: 100px;
background: green;
}
</style>
<section class="box3">
<div class="left"></div>
<div class="center">flex自适应部分</div>
<div class="right"></div>
</section>
表格:
缺点:多栏布局时,某栏高度增加,会使其他栏高度一起增加。
优点:兼容性好,当需要兼容ie8时可以用表格布局。
<style>
.box4 {
display: table;
width: 100%;
min-height: 50px;
}
.box4 div{
display: table-cell;
}
.box4 .left{
width: 100px;
min-height: 50px;
background: red;
}
.box4 .center{
min-height: 50px;
background: yellow;
}
.box4 .right{
width: 100px;
min-height: 50px;
background: green;
}
</style>
<section class="box4">
<div class="left"></div>
<div class="center">表格自适应部分</div>
<div class="right"></div>
</section>
网格布局:
优点:新技术,代码量少
缺点:兼容性不好
<style>
.box5{
display: grid;
width: 100%;
grid-template-rows:50px;
grid-template-columns:100px auto 100px;
}
.box5 .left{
background: red;
}
.box5 .center{
background: yellow;
}
.box5 .right{
background: green;
}
</style>
<section class="box5">
<div class="left"></div>
<div class="center">网格自适应部分</div>
<div class="right"></div>
</section>
-
介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?
标准模型width不计算padding和border
IE模型width计算padding和border。
box-sizing : content-box(标准模型-默认)/border-box(IE模型)
-
js中如何获取盒模型宽高?
- dom.style.width(只能取内联宽高)
- dom.currentStyle.width (浏览器渲染之后的取值,只有IE支持)
- window.getComputedStyle(dom).width (浏览器渲染之后的取值,兼容性更好)
- dom.getBoundingClientRect().width/height/left/top (常用于计算位置)
- 什么是BFC?(概念)
块级格式化上下文
-
BFC的原理
- BFC元素垂直方向的边距会发生重叠
- BFC的区域不会与浮动元素的box重叠
- BFC在页面上是一个独立的容器,外面的元素不会影响里面的元素,里面的元素也不会影响外面的元素
- 计算BFC高度时,浮动元素也会参与计算
- 如何创建BFC
- float值不为none
- position为absolute和fixed
- display是table相关以及inline-block
- overflow不为visible
- BFC使用场景?
①:解决垂直方向的边距重叠。
②:清除浮动。
③:不与浮动元素重叠。
- 如何实现下图九宫格的效果?鼠标滑过边框显示红色?
.wrap{ display: flex; flex-wrap: wrap; width: 300px; } .wrap li{ width: 100px; line-height: 92px; text-align: center; border: 4px solid #ccc; box-sizing: border-box; margin-left: -4px; margin-top: -4px; } .wrap li:nth-child(3n+1){ margin-left: 0; } .wrap li:nth-child(1), .wrap li:nth-child(2), .wrap li:nth-child(3){ margin-top: 0; } .wrap li:hover{ border-color: red; z-index: 1; }
JS相关:
-
js内置函数是什么?作用是什么?
内置函数: Object, Array , Function , Boolean , Number , String , Date , RegExp , Error , Json .
作用: 作为构造器函数
- null和undefined的区别是什么?
null表示空对象指针
undefined表示未赋值的变量
在使用==会发生类型转换.导致相等
- 哪些情况下会发生类型转换?
- ==
- 字符串拼接
- if
- 逻辑运算符
- W3C对js的规定有哪些?
- DOM ,
- BOM ,
- 事件绑定 ,
- ajax请求(包括http协议) ,
- 存储
- 箭头函数与普通函数有什么区别?
箭头函数比普通函数更加简洁
箭头函数没有this
箭头函数不存在arguments对象
箭头函数不能当作构造函数
箭头函数不能用作Generator函数 (不能使用yield命令)
- DOM的本质?DOM是哪种数据结构?DOM常用API?Attribute 和 property有何区别?
DOM本质: 浏览器拿到html代码后,DOM把html代码结构化成浏览器可识别以及js可识别的模型 . (html代码就是一个字符串,但是浏览器已经把字符串结构化成树形结构了。)
数据结构: 树形
API:
新增节点(document.createElement(none)) ,
删除节点 (parentNode.removeChild(childNode)) ,
获取父节点 (ChildNode.parentElement) ,
获取子节点(parentNode.childNode)
区别: Attribute是对html标签属性进行获取修改, property是js对象属性的获取修改
- 对于
html
的标准属性来说,attribute
和property
是同步的,是会自动更新的 - 但是对于自定义的属性来说,他们是不同步的
- 怎么理解json?
是内置的js对象,也是一种数据格式
有两个方法:
JSON.stringify({a:10,b:11})//把对象变成字符串
JSON.parse('{a:10,b:11}')//把字符串变成对象
- XML与JSON的区别?
json数据体积小 , 传递快 , 与JavaScript交互更加方便 , 容易解析 .
但是数据描述性差 , 不如XML
- 如何理解js单线程?
同一时间只能干一件事
- 什么是任务队列?
有同步任务和异步任务
seTimeout是异步任务 , 执行到异步任务要挂起
同步任务执行完再执行异步任务
- 什么是同步(对比异步)?举个例子?
同步会阻塞代码运行,异步不会
alert同步,seTimeout异步
- 什么时候需要异步?
在发生等待的情况下 , 程序仍然需要执行其他操作 , 不能阻塞程序运行
- 异步使用场景有哪些?
- 定时任务
- 网络请求: ajax , img加载, 脚本等文件加载和下载
- 事件绑定
- this有哪些使用场景?
- 作为对象属性执行
- 作为构造函数执行
- 作为普通函数执行
- call apply bind
- this指向如何判断?
- 函数直接调用指向window
- 对象调用,this指向调用的对象
- new方式直接指向它的实例对象
- 箭头函数无this,取决于包含箭头函数的普通函数this
- bind的this永远指向第一次指定的对象
- 什么是提升? 什么是暂时性死区? var,let,const有什么区别?
let,const在声明前使用会报错,这个行为是因为暂时性死区
let,const声明不会挂载在window上,不能重复声明 , 不存在变量提升 , 在块级作用域有效 , const一旦声明时必须赋值 , 且不能修改 .
- 什么是作用域? 作用域链是什么?
一个函数的父级作用域是在它定义的时候的作用域,而非它执行时候的作用域。
分为局部作用域和全局作用域,处在局部作用域里面可以访问到全局作用域的变量,而在局部作用域外面就访问不到局部作用域里面所声明的变量
在局部作用域访问变量 , 如果访问不到 , 自由变量会一直向父级作用域寻找,这样的链条称之为作用域链
- 什么是闭包呢?闭包存在的意义?有哪些优点和缺点?使用场景?
在函数A内嵌套函数B并返回 , 函数B可以访问到函数A的变量 , 函数B我们称之为闭包.
意义: 闭包是为了让我们间接的访问函数内部的变量.
优点: 封装性强,使得变量始终保持在内存中。
缺点: 内存的消耗导致的性能问题
使用场景: 函数作为返回值 , 函数作为参数传递
- 创建对象有几种方法?
- 字面量
var o={} var o=new Object() //类的实例化
- 构造函数
var O=function(name){this.name=name}; var o1=new O('o3')
- Obejct.create()
var o={name:'o'} var o2=Object.create(o)
- typeof能正确判断类型吗?instance的原理?
typeof
只能判断原始类型,null除外 (js存在的bug,在最初版本为性能考虑,使用000开头表示对象,null表示全0,所以会错误的判断null为对象)
instance原理: 沿着__proto__一层一层向上找,看是否能找到对应的prototype
- 原型与原型链的理解,它们有什么特点?
原型: 每个函数都有一个prototype属性,这个属性指向的就是原型对象. 每一个对象都有一个__proto__指向它构造函数的原型对象.
原型链:
当调用一个对象的属性时 , 如果自身对象未找到 , 会去对象的__proto__属性(隐式原型对象)里去找 , (这里的隐式原型对象指向的就是它构造函数的prototype(显示原型对象))
然后原型本身也是一个对象 , 拥有__proto__ 属性 , 所以会继续向上查找 ,一直找到Object.prototype.proto===null 这样的链条称之为原型链
特点:
原型对象上的方法是被不同实例共有的 . 当我们修改原型时,与之相关的对象也会继承这一改变。
- new 的原理是什么?通过 new 的方式创建对象和通过字面量创建有什么区别?
- 新生成了一个对象
- 链接到原型
- 绑定 this
- 返回新对象
实现一个new操作符:
function create() { let obj = {} let Con = [].shift.call(arguments) obj.__proto__ = Con.prototype let result = Con.apply(obj, arguments) return result instanceof Object ? result : obj }
区别:
更推荐使用字面量的方式创建对象(无论性能上还是可读性)。因为你使用 new Object()
的方式创建对象需要通过作用域链一层层找到 Object
,但是你使用字面量的方式就没这个问题。
- 如何声明一个类?
function Animal (){
this.name='name';
}
new Animal() //实例化
//ES6
class Animal2{
constructor(){
this.name=name;
}
}
new Animal2() //实例化
- 如何实现继承?继承有哪几种方式?
构造函数实现继承:
缺点: 无法继承父类原型对象上的属性
function Parent (){
this.name=name;
}
//Parent.prototype.say=function(){};
function Child(){
Parent.call(this);
this.type='child'
}
console.log(new Child())
原型链继承:
缺点 : 原型链上的原型对象是共用的 , 实例改变其值 , 其他实例也会发生改变
function Parent1(){
this.name='parent1';
}
function Child1(){
this.type='child1'
}
Child1.prototype=new Parent1();
console.log(new Child1()
组合继承:(寄生组合继承)
function Parent1(){
this.name='parent1';
this.play=[1,2,3];
}
function Child1(){
Parent1.call(this);
this.type='child1';
}
// Child1.prototype=new Parent1(); //父级构造函数执行了两次 (组合继承)
// Child1.prototype=Parent1.prototype; //无法判断对象的构造函数是哪一个 (组合继承1)
Child1.prototype=Object.create(Parent1.prototype);
Child1.prototype.constructor=Child1;
var c1=new Child1();
console.log(c1.constructor)
ES6 (Class类继承):
class Parent { constructor(name){ this.name=name; this.type=["parent"]; } play(){ console.log('玩些啥?') } } class Child extends Parent { constructor(name){ super(name) } } var c1 = new Child('child1'); var c2 = new Child('child2'); c1.type.push('child'); console.log(c1.type)//["parent", "child"] console.log(c2.type)//["parent"] console.log(c1.play())//玩些啥?
- 面向对象有哪几种形式?
面向对象的基本特征:多态,继承,封包(完全参考java)
构造方式:
解决了重复实例化的问题 ,又解决了对象识别的问题
function Person (name,age){ this.name = name; this.age = age; this.sayName=function(){ alert(this.name) } } var xm=new Person("xm","23"); var xh=new Person("xh","16");
工厂模式:
优点: 解决重复实例化
缺点: 无法识别到底是哪个对象的实例
//工厂模式 function createPerson(name,age){ var o = new Object(); o.name=name; o.age=age; o.sayName=function(){ alert(this.name) }; return o; } var xm = createPerson('xm','23'); var xh = createPerson('xh','16');
构造方式与工厂模式的区别:
1. 构造方式不会显示创建对象,只要将属性赋值给this,不需要return对象
2. 工厂 在方法内部创建object对象,最后返回object对象,属性和方法都是赋给object对象
原型模式:
函数本身声明为空内容,利用prototype定义一些属性及方法。
好处:让所有实例化的对象都拥有它包含的属性及方法。
function Person(){ } Person.prototype.name="xm"; Person.prototype.age="23"; Person.prototype.sayName=function(){ alert(this.name) } var xm = new Person(); xm.sayName()
原型模式的第二种方式 : json数据定义
// jSON数据定义 function Person (){ } Person.prototype={ name:"xm", age:"23", sayName:function(){ alert(this.name) } } var xm = new Person() xm.sayName()
混合模式(构造+原型):
//混合模式 function Person(name,age){ this.name=name; this.age=age; } Person.prototype={ sayName:function(){ alert(this.name) } } var xm = new Person('xm','23') xm.sayName()
- 对象类型和原始类型的区别?函数参数是对象会发生什么?
对象类型存储的是地址(指针),而原始类型存储的是值
函数参数是对象指针的副本,当参数重新分配对象时,参数的指针会发生变化.两个变量的值也会不相同.
- DOM事件模型有哪些?
捕获和冒泡
- DOM事件级别?
DOM0
缺点: 后定义的事件会被覆盖之前定义的事件
element.onclick=function(){}
DOM2
element.addEventListener('click', function(){},false)
- 什么是事件流?事件的触发过程是怎么样的?知道什么是事件代理嘛?
事件流:
捕获 > 目标元素 > 冒泡
事件触发过程:捕获:window>>>document>>>html(document.documentElement)>>>body>>>>...>>>目标元素 >然后依次冒泡到window
事件代理:
把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能
如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上
优势:
- 节省内存
- 不需要给子节点注销事件
- Event对象的常见应用
1、event.preventDefault():阻止默认事件;
2、event.stopPropagation():阻止冒泡事件;(其实也可以阻止捕获)
3、event.stoplmmediatePropagation():事件响应优先级 阻止该事件目标执行别的注册事件。(在当前响应函数中使用该方法可以阻止其他响应函数);
4、event.currentTarget:事件代理绑定的事件(父级);
5、event.target:当前被点击的元素;
- Class与普通的构造函数有什么区别?class本质?
- 本质是语法糖 , 使用prototype
- 在语法上更贴合面向对象的写法
- 在继承上更易读,理解
- 更易于写java后端语言
- es5中通过在一个函数内部用this来写,来生成一个类和实例
- es6通过class关键字声明,用constroctor生成 extends继承 super传递
class
实现继承的核心在于使用 extends
表明继承自哪个父类,并且在子类构造函数中必须调用 super
- 什么是回调函数?回调函数的缺点?如何解决?
缺点:不利于维护阅读,嵌套函数过多会很难处理错误
- Es6常用的方法有哪些?
- let和const
- 块级作用域
- 解构赋值
- 扩展运算
- promise
- 模板字符串
- 箭头函数
- symbol
- set和map
- class
- Proxy和Reflect
- Generator
- Decorators
- Module(模块化)
- promise的使用及原理? promise特点, 优缺点, promise链, Promise 构造函数执行和 then 函数执行有什么区别?
使用 : 在函数内返回一个promise对象,通过then执行下一步
特点: promise有三种状态,一旦执行不可更改
- 等待中(pending)
- 完成了 (resolved)
- 拒绝了(rejected)
promise链: promise每次then调用后返回一个全新的promise,这样就可以形成链式调用
优点: 解决回调函数不停的嵌套,造成代码难以维护.
缺点: 无法取消promise, 错误需要回调函数捕获
- 说说深拷贝?浅拷贝?
- 浅拷贝是指只拷贝所有属性到新对象,如果属性值是对象,只拷贝地址.
- 浅拷贝通过object.assign(),扩展运算符...来解决
var newObj = Object.assign({}, obj);
var newObj = {...obj};
通过JSON.parse(JSON.stringify(object))来解决
var newObj = JSON.parse(JSON.stringify(obj));
但是:
- 会忽略
undefined
- 会忽略
symbol
- 不能序列化函数
- 不能解决循环引用的对象
简易版深拷贝:
function deepClone(obj){ function isObject(o){ return (typeof o === 'object' || typeof o === 'function') && o !==null; } if(!isObject(obj)){ throw new Error('非对象'); } var isArray = Array.isArray(obj); var newObj = isArray ? [...obj] : {...obj}; Reflect.ownKeys(newObj).forEach(key => { newObj[key] = isObject(newObj[key]) ? deepClone(newObj[key]) : newObj[key]; }) return newObj; } var obj = { name:"AAA", job: { name: "FFF", money: 12000 } } var cloneObj = deepClone(obj); obj.job.money = 13000; console.log(obj.job.money);//13000 console.log(cloneObj.job.money)//12000
- 为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?
- Proxy 可以实现什么功能?
- async 及 await 的特点,它们的优点和缺点分别是什么?await 原理是什么?
一个函数如果加上 async
,那么该函数就会返回一个 Promise
- Generator 是什么?
- 什么是执行栈?
存储函数调用的栈结构,遵循先进后出的原则
EventLoop:
当遇到异步代码的时候,会被挂起在Task队列中,一旦执行栈为空,就会从Task中拿出需要执行的代码执行,所以本质上讲JS中的异步还是同步行为。
异步任务执行顺序:
- 宏任务(
script
、setTimeout
、setInterval
、setImmidiate
、I/O
、UI Rendering
)可以有多个队列 - 微任务(
procress.nextTick
、Promise.then
、Object.observe
、mutataionObserver
)只能有一个队列
当执行栈执行完毕后,会首先执行微任务队列,当微任务队列执行完毕再从宏任务中读取并执行,当再次遇到微任务时,放入微任务队列。
Vue相关:
- 了解MVVM框架吗?
Vue.js
- 谈谈你对MVVM的认识? 和MVP有什么区别? jQuery和Vue的区别是什么?
MVP:
jquery使MVP模型
view:视图 Model:数据 Presenter:控制器
View发出一个事件交给Presenter,控制器调取model或者直接操作View,Presenter是核心 ,大部分的工作都在Presenter层 (面向DOM开发)
MVVM:
vue使用的MVVM模型
View (视图) Model(数据) ViewModel(vue.js框架核心)
大部分工作是在操作M层,DOM操作显著减少,使用MVVM是面向数据进行编程 (面向数据开发)
- 双向绑定是什么原理,可以写出来吗?
数据驱动页面(data->view),页面变了也能将js中保存的变量做相应的改变(view->data),这个过程是自动的。
data = view 是利用es5 api Object.defineProperty, 3大框架底层都是依靠它,它能监听到data变化,并有一个会调函数。
view = data 是input事件,只不过框架执行了我们看不到。
- 使用了什么设计模式?
观察者模式
- 生命周期是什么?
beforeCreate: Vue实例创建前 (获取不到props和data数据)
created: Vue实例创建后 (可以获取数据 , 但组件还未被加载)
beforeMount: 模版数据挂载前 (开始创建VDOM)
mounted: 模版数据挂载后 (将VDOM渲染为真实的DOM并且渲染数据 , 挂载组件)
beforeUpdate: 数据更新前
updated: 数据更新后 ((keep-alive组件激活前后)
beforeDestroy: Vue实例销毁前 (适合移除事件 , 定时器)
destroyed: Vue实例销毁后
activated: keep-alive组件激活时
deactivated: keep-alive组件停用时
errorCaptured: 捕获子 , 孙组件发生错误时触发 (2.5+版本增加)
- vue如何做响应式?
是通过object.definepropotype的get和set方法来实现的
- vue怎么实现阻止冒泡?
stop修饰符
.stop
阻止冒泡.prevent
阻止浏览器的默认行为、.capture
事件捕获模式.self
只有触发元素是自身时才执行的事件.once
只执行一次的事件.passive
事件行为立即触发,而不会等待
- v-if与v-show的区别?
v-if为假时不会渲染 , v-show始终都会被渲染 , 通过display-none和display-block切换 .
- computed与watch和methods有什么区别?
computed
是计算属性,依赖其他属性来计算值,并且 computed
的值有缓存,只有当计算值变化才会返回内容。
watch
监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。
methods 每次都会进行计算
- 父子组件如何进行通信?
父组件通过属性的形式传递数据给子组件,子组件通过props来接收数据 , 通过 emit
发送事件传递数据给父组件
子组件不能直接修改 props
, 而是必须通过发送事件的方式告知父组件修改数据 (单向数据流)
- vue如何获取DOM元素?
(1)普通标签设置ref 通过this.$refs.ref名称获取DOM元素 例如 this.$refs.hello【其为获取ref为hello的节点】
(2)组件上的ref获取的组件上的引用
- props与非props特性
props特性:
1、在子组件中有props来接收父组件传来的数据
2、可以在子组件中使用传来的数据,但是父组件设置的属性不会在最外层的元素上显示
非props特性:
1、无法接收父组件传来的数据(因为子组件中没有相应的props来接收父组件传来的数据);
2、父组件设置的属性会在其最外层的元素上显示
- 写一个clone函数,可以复制原始类型的值.
- 完善通用绑定事件函数
function on(elem, type, selector, fn) {
if (fn == null) {
fn=selector;
selector = null;
}
elem.addEventListener(type, function (e) {
var target;
if (selector) {
target = e.target;
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
- 以下代码如何实现点击列表显示相应的index值?
<ul id="test">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
使用jq:
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
<script>
$('#test').on('click','li',function(e){
alert($(this).index());
})
使用js:
- 手写call函数
Function.prototype.myCall = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(...args) delete context.fn return result }
- 给定一段URL和参数的名称,获取此参数的值
var url = 'https://www.baidu.com/s?id=123&name=why&phone=13876769797'; function getQuery(name){ var strs = ''; var index = url.indexOf('?'); if(index!=-1){ strs = url.substring(index+1).split('&') } for (let index = 0;index < strs.length; index++){ var splitItem = strs[index].split('='); if(splitItem[0]==name){ return splitItem[1] } } } console.log(getQuery("name"))//why
- 写一个方法 , 去除掉重复元素...
//第一种 var arr = [0,2,3,4,4,0,2]; var obj ={}; var tmp =[]; for(var i = 0;i<arr.length;i++){ if(!obj[arr[i]]){ obj[arr[i]]=1; tmp.push(arr[i]); } } console.log(tmp); //第二种 var arr2 = [0,2,3,4,4,0,2], tmp2 = []; for(var i=0;i<arr2.length;i++){ if(tmp2.indexOf(arr2[i]) < 0){ tmp2.push(arr2[i]) } } console.log(tmp2) //第二种 var arr3 = [0,2,3,4,4,0,2]; var tmp3 = arr3.filter(function(element,index,self){ return self.indexOf(element) === index; });
- 说说你做过最复杂的js代码...
- 说说你做过最复杂的项目
- 说说你的职业规划
- 做一下自我介绍
-
你对当前待遇的期望...
-
如果达不到期望,你会如何选择?
如果达不到期望 , 那可能是我自身技术水平还不行 , 我会更加努力学习的.