不知不觉,又到校招时间啦。这份前端面试宝典,是我在去年,在无数个月的黑夜下,奋笔疾书,呕心沥血,织帘诵书,映雪读书,废寝忘食,停停写写,巴拉巴拉能量,集大成之作。99%内容都是按自己的理解纯手打与各处收集的题与答案。包括我自己以前在面试中经常遇到的问题我也加入了。本着社区分享快乐原则,现在分享给各位同学,希望你能有所收获。当然,都是一些比较基础简单的内容,如果发现有误的地方,大概是我头昏眼花了,欢迎在评论区指出,一起讨论。毕竟是停停写写的,可能会有许多题没有答案,或者答案不严谨的,望见谅,我也不去检查然后一个个删了,就保留着,时刻警醒自己,当然也可以自己补全,哈哈~
文章目录
- 一. HTML、CSS 基础得记
-
-
-
-
- 1.p 标签中的文字如何水平垂直居中
- 2.怎样将两个 span 标签在 p 标签内水平居中
- 3.子元素父元素不给宽高,怎么垂直水平居中
- 4.怎么将多列字体大小不同的子元素之间互相居中排列
- 5.怎么实现移动端适配
- 6.移动端如何实现 0.5px 边框
- 7.如何解决加载 css 出现不断闪烁空白问题
- 8.说说你知道的选择器
- 9.css3 新特性
- 10.H5 新特性:
- 11.em 和 rem:
- 12.常用清除浮动方法:
- 13.说一说 BFC :
- 14.css 可继承样式:
- 15.link 与@import 区别:
- 16.行内元素的 padding、margin:
- 17.flex 布局:
- 18.媒体查询@media:
- 19.css 画三角形:
- 20.css 选择器权重:
- 21.外边距合并:
- 22. rgba()与 opacity 透明效果区别:
- 23. Doctype 的作用 :
- 24. CSS 属性触发 GPU 硬件加速:
- 25.audio 与 video 的常用属性与方法:
- 26.canvas 的常用属性与方法:
- 27. vertical-align:
- 28.文本溢出隐藏省略号:
- 28. SVG 矢量图:
- 29.回流 reflow 与重绘 repaint:
- 30.常用 sass 用法:
- 31.盒子模型:
- 32.bootstrap 栅格布局:
- 33.href 与 src 的区别:
- 34.PC 端或移动端如何设置适配屏幕宽度:
- 35.ifram 标签使用规则:
-
-
-
- 二 . JS 地基要牢
-
-
-
-
- **1.说说 js 中的数据类型**
- **2.说说 var let const 的区别**
- **3.说说你还知道哪些 ES6 特性**
- **4.说说原型以及原型链**
- **5.说说你是如何理解闭包的**
- **6.说说数组、字符串、对象 中你用到的哪些方法**
- **7.说说 flat 方法如何将多维数组变为一维数组**
- **8.说说 DOM 事件流**
- **9.说说 stopPropagation preventDefault 的区别**
- **10.如何设置 localStorage 存活一定时间**
- **11.如何实现人为垃圾回收 强制 delete 设置为 null**
- **12.如何字符串转数值型**
- 13.如何判断一串字符串完全是由数字组成的字符串
- 14. 说一说 DOM 和 BOM
- 15.typeof 箭头函数结果是什么?如何判断一个变量是普通还是箭头函数?
- 16.call 、apply 、 bind
- 17.js 继承有哪些 :
- 18. 常用操作节点方法:
- 19.new Date()方法:
- 20.深浅拷贝:
- 21.数组去重方法:
- 22.快速打乱一个数组排序:
- 23. addEventListener() 与 attachEvent()区别:
- 24.已知有字符串 foo="get-element-by-id",写一个 function 将其转化成驼 峰表示法"getElementById"
- 25.JavaScript 模块化规范:
- 26.拖拽
- 26. proxy 构造器代理:
- 27.反射 reflect:
- 28.谈一谈 this:
- 29.js 原型作用:
- 30.什么是 js 垃圾回收机制不能回收的(内存泄露):
-
-
-
- 三. 计算机网络
-
-
-
-
- **1.说说浏览器渲染的基本过程**
- **2.说说三次握手四次挥手的过程**
- **3.说说 http 的两大存储**
- **4.http 和 websocket 有什么区别**
- **5.http 和 https 的区别**
- **6.https 如何实现加密的**:
- **7.状态码 304 是什么意思**
- 8.简单说一说 http:
- 9.get 与 post 请求区别:
- 10. 常见 http 状态消息含义
- 11. cookie 与 session:
- 12.cookie、sessionStorage、localStorage 区别:
- 13. 跨域与同源策略:
- 14.异步执行机制【事件循环机制 EventLoop】,宏任务与微任务:
- 15.offsetWidth、offsetHeight,clientWidth、clientHeight,scrollwidth,scrollHeight 区别:
- 16. 服务端渲染与客户端渲染区别(SPA SEO SSR):
- 17.DNS 解析:
- 18.TCP 与 UDP 区别:
- 19.谈谈 window.history:
- 20.谈谈 webSocket:
- 21. http 协议中与资源缓存相关的协议头有哪些?
- 22.http 各个版本对比:
- 23.浏览器输入 url 后经历的过程:
- 24.token 的作用:
- 25.常见的宏任务和微任务:
-
-
-
- 四. vue的基础得会
-
-
-
-
- **1.说说你用过组件通信的方式**
- **2.vue 里面有个虚拟 dom,你了解吗**
- **3.用过 nextTick 吗,原理是什么**
- **4.为什么 data 是个函数**
- **5.v-for 循环中的 key 是干嘛的**
- **6.了解 keep-alive 吗,说说**
- 7.八大生命周期:
- 8.watch 与 computed 区别:
- 9.说一下 v-on
- 10.说一说单向数据流与双向数据流:
- 11. 说一说 vue 插槽:
- 12. 说一说导航守卫:
- 13.说一说路由:
- 14. 路由懒加载:
- 15.双向数据绑定原理:
- 16.template 与 render 函数的区别:
- 17.异步组件加载:
- 18.函数式组件:
- 19. query 与 params 区别
- 20.vue 组件化构建:
- 21.vue 事件修饰符:
- 22.谈谈 Vuex 状态机:
- 23.vue2 与 vue3 区别:
- 24.keep-alive 缓存组件:
- 25.vue 自定义指令:
- 26.filter 过滤器:
- 27.路由模式:
-
-
-
- 五. 前端基础延伸的会
-
-
-
-
- promise 封装 ajax 请求:
- promise 链式调用 :
- 1.Promise.all
- 2.Promise.race:
- Promise 扩展:
- 3.history 与 hash 的区别:
- 4.前端性能如何优化:
- 5.说一说 JSON:
- 6. async 与 await :
- 7. 写一个基本的 ajax 请求:
- 8.vue 性能优化:
- 9.原型重构数组方法:
- 10.Git 常用命令:
- 11.Linus 常用命令:
- 12.正则表达式:
- 13.axios 常用方法: get、post、put、patch、delete
- 14.解构赋值:
- 15.常见的异步编程:
- 16.防抖与节流:
- 17. js 函数柯里化:
- 18.图片懒加载:
- 19. JQuery 一些用法:
- 20. axios 拦截器:
- 21.axios 请求模块化封装:
-
-
- 六. 扩展:
-
一. HTML、CSS 基础得记
1.p 标签中的文字如何水平垂直居中
1.flex 布局,display:flex;justify-content:center;align-items:center;
- 先 text-align:center,再 line-height:跟盒子高一样。
2.怎样将两个 span 标签在 p 标签内水平居中
3.子元素父元素不给宽高,怎么垂直水平居中
4.怎么将多列字体大小不同的子元素之间互相居中排列
5.怎么实现移动端适配
- @media 媒体查询实现
- booststrap 框架
- 动态 rem 实现
6.移动端如何实现 0.5px 边框
1.把元素的伪类高度设为 1px,背景渐变,一半有颜色,一半透明。
2.transform:scale()来达到压缩一半的目的。
7.如何解决加载 css 出现不断闪烁空白问题
8.说说你知道的选择器
类选择器、id 选择器、后代选择器、兄弟选择器、子代选择器、伪类选择器、属性选择器
9.css3 新特性
过渡 transition、动画 animation、形状转换 transform、阴影 box-shadow、flex 布局、栅格布局 grid、媒体查询@media、伪类选择器。。。
10.H5 新特性:
- 1.新增语义化标签 header、article、footer、section、nav
- 2.本地存储 webStorage,包括 sessionStorage、localStorage 。和 indexedDB。
- 3、input 新增的属性:
- 4.audio 和 video 标签。
- 5、data-*自定义属性。。。
- 6.canvas 画布
- 7.拖拽 drag
语义化标签好处:便于阅读理解维护。页面结构化,便于浏览器搜索引擎解析。
11.em 和 rem:
都是长度单位。区别是 em 相对于父元素字体大小。rem 相对于 HTML 根元素大小。
12.常用清除浮动方法:
因为子元素浮动脱离文档流所以父元素高度塌陷。
1.父元素 overflow:hidden。2.父元素 ::after{content:‘’;display:block;clear:both} 3.浮动元素同级最后加一个空盒子,设置 clear:both。
13.说一说 BFC :
格式化上下文,一个隔离的独立容器。容器内元素布局不影响外界。
开启条件:
1.浮动。
2.position不为static、relative
3.oveflow不为visible
4.display为inline-blick.table-cell.flex.table-caption.inline-flex
BFC 的区域不会与 float box 重叠。计算 BFC 的高度时,浮动元素也参与计算。
14.css 可继承样式:
font、color、line-height、cursor、text-align、visibility、text-indent、letter-spacing、list-style
15.link 与@import 区别:
- link 是 HTML 标签,不仅可以加载 css,还可以定义 rss、rel 等。@import 是 css 语法规则,只有样式导入作用。
- link 标签引入时同 css 同步加载。@import 将在页面加载完毕后加载。
- link 无兼容,@import 是 css2 出的,ie5+支持。
- js 可以操作 link。js 操作不了@import。
16.行内元素的 padding、margin:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3IquHQTj-1657174227824)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210827105703891.png)]
17.flex 布局:
容器属性:
flex-direction:row|column|row-reverse|column-reverse;
flex-wrap:nowrap|wrap|wrap-reverse;
flex-flow: row nowrap ... flex-direction与flex-wrap简写;
justify-content:flex-start|flex-end|center|space-between|sapce-around|space-evenly;
align-items:stretch|flex-start|flex-end|center|baseline;
align-content:stretch|flex-start|center|flex-end|space-around|space-between|space-evenly;
元素属性:
order: 0|1|2....; 越大排越靠前
flex-grow:0|1|...; 占据剩余空间
flex-shrink:1|0 空间不足是否缩小,默认缩小
flex-basis:auto| 项目宽度
flex: 0 1 auto | 上面3个简写
align-self:auto|flex-start|flex-end|center|baseline|stretch
align-self:auto|stretch|center|flex-start|flex-end|baseline|initial|inherit;子项单独在侧轴(纵轴)方向上的对齐方式。
18.媒体查询@media:
设备类别 | 尺寸区间 | 平常设置的大小 |
---|---|---|
手机 | <768px | 100% |
平板 | >=768px - - - < 992px | 750px |
桌面显示屏(中等屏幕) | >=992px - - - < 1200px | 970px |
大桌面显示器(大屏幕) | > = 1200px | 1170px |
<link rel="stylesheet" media="(min-width:800px) href="example.css">
// @media 媒体类型 and (媒体属性) {
。。。属性 }
<style>
@media screen and (min-width:500px){
body{
color:red;}
}
@media screen and (min-width:1200px){
}
</style>
19.css 画三角形:
div{
width:0;
height:0;
border: 10px solid transparent;
border-bottom:10px solid red;
}
div{
width:0;
height:0;
border-bottom:10px solid red;
border-left:10px solid transparent;
border-right:10px solid transparent;
}
-----------------------------------------------
clip-path 属性使用裁剪方式创建元素的可显示区域。
--------------------------------------------
canvas画布画
20.css 选择器权重:
第一等级:代表 内联样式,如 style="",权值为 1,0,0,0;
第二等级:代表 ID选择器,如 #id="", 权值为 0,1,0,0;
第三等级:代表 calss | 伪类 | 属性 选择器,如 .class | :hover,:link,:target | [type], 权值 0,0,1,0;
第四等级:代表 标签 | 伪元素 选择器,如 p | ::after, ::before, ::fist-inline, ::selection, 权值 0,0,0,1;
此外,通用选择器(*),子选择器(>), 相邻同胞选择器(+)等选择器不在4等级之内,所以它们的权值都为 0,0,0,0;
权值计算 公式:
权值 = 第一等级选择器*个数,第二等级选择器*个数,第三等级选择器*个数,第四等级选择器*个数;
比较规则就是三点
1.先从高等级进行比较,高等级相同时,再比较低等级的,以此类推;
2.完全相同的话,就采用 后者优先原则(也就是样式覆盖);
3.css属性后面加 !important 时,无条件绝对优先(比内联样式还要优先);
21.外边距合并:
在 CSS 当中,相邻的两个盒子(可能是兄弟关系也可能是祖先关系)的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。分为兄弟合并和父子合并。
解决方法:
父子级: 1.给父元素添加边框属性 2.给父元素/子元素添加浮动属性 3.给父元素/子元素添加 position:absolute 属性 4.给父级元素设置 overflow:hidden 属性 5.给父子级添加 display:inline-block 属性 6.将本应该设置给子元素的外边距设置给父元素的内边距
兄弟级:
解决:将本应该设置给两个元素的外边距设置给一个元素
合并规则: 1.两个数值均为正数时,取较大一个 2.两个数值均为负数时,取较小的一个 3.当两个数值一正一负时,直接相加
22. rgba()与 opacity 透明效果区别:
opacity 是作用在元素上的,元素内所有内容都透明。它及其所有子元素都透明了。
rgba()是作用与颜色,只是颜色上透明。设置 rgba()的子元素不会继承透明。
23. Doctype 的作用 :
< !DOCTYPE > 用于告知浏览器以何种模式来渲染文档。h5 为< !DOCTYPE html >。( HTML5 没有严格和混杂之分)
严格模式:页面排版及 JS 解析采用改浏览器支持的最高标准。
混杂模式:不严格按照标准执行,为了兼容旧浏览器。
24. CSS 属性触发 GPU 硬件加速:
transform、opacity、filter 、Will-change 这些属性可以触发。
浏览器接收到页面文档后,会将文档中的标记语言解析为 DOM 树。DOM 树和 CSS 结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到 GPU 形成渲染纹理,而图层在 GPU 中transform
是不会触发 repaint(重绘) 的,最终这些使用 transform
的图层都会由独立的合成器进程进行处理。CSS transform
会创建了一个新的复合图层,可以被 GPU 直接用来执行 transform
操作。
ransform 属性不会触发浏览器的 repaint(重绘),而绝对定位 absolute 中的 left 和 top 则会一直触发 repaint(重绘)。使用 css3 硬件加速,可以让 transform、opacity、filters 这些动画不会引起回流重绘 。但是对于动画的其它属性,比如 background-color 这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
如果你为太多元素使用 css3 硬件加速,会导致内存占用较大,会有性能问题。在 GPU 渲染字体会导致抗锯齿无效。这是因为 GPU 和 CPU 的算法不同。因此如果你不在动画结束的时候关闭硬件加速,会产生字体模糊。
25.audio 与 video 的常用属性与方法:
video:
<video src="..." controls autoplay poster="..." loop muted></video>
constrols 是控制条 , loop 是循环播放 , autoplay 是自动播放 , poster=‘’ 封面 放封面地址 , muted 静音 ,width,height。
方法 .play() 播放 , .pause() 暂停, .paused 切换是否是暂停状态, .volume 控制音量 , .currentTime 当前播放秒数 ,.playbackRate 倍速播放
audio:
<audio src="..." controls autoplay loop muted></audio>
属性跟方法跟 video 差不多一样的。
26.canvas 的常用属性与方法:
1.获取画布:
var canvas=document.querySelector('canvas');
var ctx=canvas.getContext('2d');
-----
ctx.clearRect(0,0,canvas.width,canvas.heigth);
2.绘制矩形:
ctx.fillstyle = 'red';
ctx.fillRect(0,0,100,100);
-----------
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
ctx.strokeRect(0,0,100,100);
3.绘制圆:
ctx.arc(50, 50, 10, 0, Math.PI, false);
// x y r 开始弧度 结束弧度 圆的方向是否是逆时针路径
ctx.fill();
// ctx.stroke()绘制轮廓
4.渐变:
// ---------------------线性渐变
var g = ctx.createLinearGradient(0, 0, 400, 400);
// 声明一个渐变对象 第一个参数渐变x轴开始位置 第二个参数渐变y轴开始位置 第三个参数渐变结束x轴位置 第四个参数渐变结束y轴位置
g.addColorStop(0, "red");
g.addColorStop(0.5, "cyan");
g.addColorStop(1, "yellow");
// 将渐变色给到填充样式
ctx.fillStyle = g;
//-----------------------径向渐变
var g = ctx.createRadialGradient(200, 200, 50, 200, 200, 200);
// 声明一个渐变对象 前三个参数开始圆心x轴开始位置 y轴开始位置 半径 后三个参数结束圆心x轴开始位置 y轴开始位置 半径
5.绘制线段:
context.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineCap = "round";
ctx.lineTo(100, 100);
ctx.lineTo(200, 150);
ctx.closePath();
6.绘制文本:
ctx.font = "20px fangsong";
ctx.fillText("hello", 0, 0);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
strokeText(str, x, y);
- 常用变形:
ctx.save();
//将当前的绘画状态进行保存并存入状态栈
ctx.translate(50, 50);
ctx.scale(x, y);
//x:代表的是水平方向上的放大倍数 y:代表的是垂直方向上的放大倍数(如果想要缩小,参数设置为0-1之间的值)
ctx.rotate(angle);
//angle:旋转角度,旋转的中心点就是坐标轴的原点,默认按照顺时针进行旋转,想要进行逆时针旋转,角度设置为负数即可
ctx.restore();
//该方法每次调用只会回滚到上一次save的状态
8.绘制图像:
var img = new img();
img.src = "...";
img.onload = function () {
ctx.drawImage(img, 0, 0, 200, 200);
};
9.绘制视频:
var canvas = document.querySelector("canvas");
// 获取上下文
var context = canvas.getContext("2d");
var video = document.querySelector("video");
draw();
function draw() {
context.drawImage(video, 0, 0, 200, 200);
// 请求动画帧
requestAnimationFrame(draw);
}
27. vertical-align:
经常用于设置图片或者表单【行内块级元素】和文字垂直对齐。
- basesline:默认,元素放置在父元素的基线上
- top:把元素的顶端与行中最高元素的顶部对齐
- middle:把此元素放置在父元素的中部
- bottom:把元素的顶部与行中最低的元素的顶部对齐
解决图片底部默认空白缝隙问题,给图片添加 vertical-align:middle;
28.文本溢出隐藏省略号:
单行:
{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
多行:
{
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
28. SVG 矢量图:
SVG 能够绘制一些 CSS 难以做到的复杂图像和动画。而且放大不失真,为矢量图。SVG 绘图时,每个图形都是以 DOM 节点的形式插入到页面中。
29.回流 reflow 与重绘 repaint:
回流:当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建的过程叫回流(改变大小、布局)。
重绘:当渲染树中的一部分元素需要更新属性,如改变元素的外观、风格,而不影响布局的重新渲染的过程叫重绘(改变样式)。
注意:回流一定会触发重绘,而重绘不一定会回流。
30.常用 sass 用法:
31.盒子模型:
。。。
设置盒子模型:
box-sizing:border-box ; 边框盒子
box-sizing:centent-box;内容盒子
box-sizing:inherit; 继承父元素
32.bootstrap 栅格布局:
<div class="container">
<div class="row">
<div class="col-lg-3">1</div>
<div class="col-lg-3">2</div>
<div class="col-lg-3">3</div>
<div class="col-lg-3">4</div>
</div>
</div>
如果全部子盒子加起来不够 12 列咋办,那么.container 整体就不会分为 12 列,有多少就多少。如果全部子盒子加起来超过 12 列咋办,多的会另起一行显示。可以同时为一列指定多个设备的类名。
<div class="container">
<div class="row">
<div class="col-lg-3 col-md-4">1</div>
<div class="col-lg-3 col-md-4">2</div>
<div class="col-lg-3 col-md-4">3</div>
<div class="col-lg-3 col-md-4">4</div>
</div>
</div>
行( row )必须放到 container 布局容器里面,我们实现列的平均划分需要给列添加类前缀。
列( column)大于 12,多余的“列(column)"所在的元素将被作为一个整体另起一行排列,每一列默认有左右 15 像素的 padding 值。
可以同时为一列指定多个设备的类名,以便划分不同份数例如 class= “col-md-4 col-sm-6”
栅格系统内置的栅格系统将内容再次嵌套。简单理解就是一个列内再分成若干份小列。套娃。:
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="row">
<div class="col-md-6">1.1</div>
<div class="col-md-6">1.2</div>
</div>
</div>
<div class="col-md-4">2</div>
<div class="col-md-4">3</div>
</div>
</div>
使用.col-md-offset-* 类可以将列向右侧偏移。这些类实际是通过使用选择器为当前元素增加了左侧的边距( margin )。当然什么屏幕尺寸就用什么前缀。
<div class="container">
<div class="row">
<div class="col-md-4">左</div>
<div class="col-md-4 col-md-offset-4">右</div>
</div>
</div>
33.href 与 src 的区别:
- 请求资源类型不同。href 是超文本引用的简写,src 是指向的资源下载并引用到当前文档。
- 作用结果不同。href 是为当前文档和引用资源建立联系。src 是替换当前元素。
- 浏览器的解析方式不同。herf 引用的资源,浏览器会将其识别为 CSS 文档,并行下载资源并且不会停止对当前文档的处理。当浏览器解析到 src 时,会暂停其他资源的下载和处理,直接将该资源下载,编译,执行完毕,图片和框架也是如此,类似于将所指资源应用到当前内容。
34.PC 端或移动端如何设置适配屏幕宽度:
- 使用百分比长度单位。vw、vh、vm、em、rem、% 。优点:一套 css 代码可适应所有屏幕宽度不同的手机。
vw:相对于视口的宽度,视口被均分为 100 单位的 vw。将当前屏幕宽度均分为 100 份,1vw 即为当前屏幕宽度的 1%;**优点:**vw 单位长度准确分明,常用此单位,推荐使用此单位。
**vh:**相对于视口的高度,视口被均分为 100 单位的 vh;即将当前屏幕高度均分为 100 份,1vh 即为当前屏幕高度的 1%;基本不用此单位。
**vm:**相对于视口的宽度或高度,取决于哪个更小,视口宽度或高度中更小的那个被均分为 100 单位的 vm;
**em:**相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于上一个父元素的默认字体尺寸。
**rem:**相对长度单位,相对于根元素(即 html 元素)内文本的字体尺寸。rem 继承且只继承 html 标签的字体大小。同样的,此单位若要用于屏幕自适应,需与 vw 配合使用设置根元素的字体大小。必须在 css 中进行字体设置:html{font-size:4vw;},方可实现屏幕自适应。常用此单位,推荐使用此单位。
- 使用固定长度单位 px ,再使用 js 根据当前屏幕宽度与固定宽度计算比例,进行网页缩放,来实现移动端屏幕自适应 。
35.ifram 标签使用规则:
https://zhuanlan.zhihu.com/p/88809313
二 . JS 地基要牢
1.说说 js 中的数据类型
1.值类型:number、string、boolearn、undefined、null、Symbol
2.引用类型:Object、Array、Function
2.说说 var let const 的区别
1. 作用域不同,var是函数作用域,let是块级作用域。
2. var有变量声明提升,let没有,同时let存在暂时性死区。暂时性死区就是如果在let声明语句之前使用变量,会报Uncaught ReferenceError错误。
3. var可以重复声明相同变量名,let和const不可以重复声明。
4.const声明常量。块级作用域。没有变量提升。必须赋初始值。
5.let和const不能和var使用一样变量名。
3.说说你还知道哪些 ES6 特性
1.const 声明常量。也是块级作用域,也不能变量提升,同时不能修改,但是引用类型的值可以修改。
-
变量解构赋值。数组解构,如 [a,b] = [1,2],相当于 a=1,b=2;对象的解构赋值,{name,age} = {name:‘jack’,age = 18} 相当于 name=Jack,age = 18;注意的是变量名与对象里属性名会一一对应,不管顺序如何。
-
模板字符串,可换行与拼接变量:
` result:${sum} `
- 箭头函数。如 let a = (a,b) => a + b ;注意的是 this 指向的是声明时所处作用域下的 this。
- 扩展运算符。arr=[1,2,3] 则 […arr]相当于[1,2,3] ,可用来快速拼接数组。
- Symbol,表示独一无二的值。let a = Symbol(‘hello’) 。
- promise,解决异步编程新方案。
- set 方法,类似数组,但成员都是唯一,可以拿来数组去重。如 arr=[1,2,3,3] arr2=[…new Set(arr)]。
- class 类。constructor 定义构造函数初始化。static 定义静态方法与属性。
- 。。。。略。
4.说说原型以及原型链
- prototype,通常称为原型,它是函数的一个属性,类型为对象。
__proto__
,通常称为原型链,它是对象的一个属性,类型也是对象。- 概念就是对象的
__proto__
保存着该对象构造函数的 prototype。 - 原型链继承机制。如果在这个对象里找不到该属性,那么他会通过**
proto
**为连接点向上寻找,找到它构造函数的 prototype 看看有没有,没有的话通过 prototype 里的__proto__
为连接点继续往上找,一直找到 Object.prototype 为止。 - 原型链继承机制中,只会返回先找到的值。
- 底层规定
Function.__proto__ === Function.prototype
- 扩展,hasOwnProperty()方法可以查看属性是否在对象上,且不是原型链上的对象。也可用
in
判断,in 的话原型链上的也算。 - constructor 属性返回对创建此对象的数组函数的引用。
5.说说你是如何理解闭包的
1.闭包简单来说就是一个函数定义时和它周围状态引用捆绑起来的一个组合。
优点:1.能够读取函数内部的变量 2.让这些变量一直存在于内存中,不会在调用结束后,被垃圾回收机制回收。
2. 同时,闭包和函数的定义有关,而不是和函数调用时有关。跟他相反的是this和函数调用时有关,箭头函数除外。
3. 解决闭包内存泄露:退出函数前将局部变量赋值为 null 。
如:return function(){console.log(i++) i=null; }
for(var i=0 ; i< 5 ;i++){
(function (j){
console.log(j);
})(i);
}
6.说说数组、字符串、对象 中你用到的哪些方法
Array:
1. push(),数组末尾添加**一个或多个**元素,返回长度,改变原数组。
2. pop(),数组末尾删除**一个**元素并返回,改变原数组。
3. unshift(),数组开头添加**一个或多个**元素,返回长度,改变原数组。
4. shift(),数组开头删除**一个**元素并返回,改变原数组。
5. concat(),合并多个数组,返回新数组,不改变原数组。
6. join(),将数组变为字符串,返回一个字符串,不改变原数组。
7. slice(),提取数组部分元素,返回新数组,不改变原数组。
8. splice(),删除或替换或添加数组元素,改变原数组。
9. sort(),数组排序,改变原数组。
10. reverse(),数组前后颠倒,改变原数组。
11. forEach()、filter()、返回新数组,不改变原数组。
12. every()、some()、map()、indexOf()略。
13.IndexOf()、lastIndexOf()
14.Array.from({'0':'0','1':'1',length:2}) 将类数组对象转为数组。
String:
1.toLowerCase();把字符串转为小写,返回新的字符串。
2.toUpperCase();把字符串转为大写,返回新的字符串。
3.charAt();返回指定下标位置的字符。如果index不在0-str.length(不包含str.length)之间,返回空字符串。
4.charCodeAt();返回指定下标位置的字符的unicode编码,这个返回值是 0 - 65535 之间的整数如果index不在0-str.length(不包含str.length)之间,返回NaN。
5.indexOf();返回某个指定的子字符串在字符串中第一次出现的位置。如果子字符串没有找到,返回-1。第二个参数表示从哪个下标开始查找,没有写则默认从下标0开始查找。
6.lastIndexOf();返回某个指定的子字符串在字符串中最后出现的位置。。。
7.substr();返回从指定下标开始指定长度的的子字符串。
8.substring();提取字符串中介于两个指定下标之间的字符。
9.split();把字符串分割成字符串数组。参数可字符串或正则。
10.replace(‘要替换’,‘替换成’);在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
11.match();返回所有查找的关键字内容的数组。
Object:
1.Object.keys(obj):会返回一个给定对象的自身可枚举属性组成的数组.
2.Object.values(obj):返回一个给定对象自身的所有可枚举属性值的数组.
3.Object.entries(obj):返回一个给定对象自身可枚举属性的键值对数组.
4.hasOwnProperty():返回一个布尔值,指示对象自身属性中是否具有指定的属性.
5.toString():返回一个表示该对象的字符串。
6.Object.assign(obj1,obj2,obj3):用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
7.Object.create():创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。即创建一个以指定的对象为原型的子对象。
8.Object.defineProperty():会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
9.Object.defineProperties():直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
10.var obj = Object.assign({},oldObj);
将源对象(source)的所有可枚举属性,复制到⽬标对象。这相对于浅拷贝。
11.Object.is(+0,-0) 比较两个值是否相等,类似比较运算符(===)。特殊的是 +0 不等于 -0 ,⼆是 NaN 等于⾃身。
7.说说 flat 方法如何将多维数组变为一维数组
1. 称为数组扁平化处理,直接 `arr.flat()`,返回一个新数组。默认只拉平一层。
2. `arr.flat(2)`, 参数可以填数字,填几就拉平几层。
3. `arr.flat(Infinity)` 参数写Infinity,不管几层都拉为一层。 注意Infinite的I是大写。
原生实现:
一般而言(元素为对象的话不行):
var newArr = arr.toString().split(',');
要换成数字的话:
var newArr = arr.toString().split(',').map(item=>Number(item));
8.说说 DOM 事件流
捕获阶段--------->目标阶段-------->冒泡阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waE2Ldxc-1657174227827)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210821223959319.png)]
事件委托(事件代理):利用事件冒泡的特性,将本应该注册在子元素上的处理事件注册在父元素上。
然后通过 e.target 就能知道它点击的是哪个子元素。
addEventListener 的第三个参数设置为 true 和 false 的区别已经非常清晰了。true 表示该元素在事件的“捕获阶段”(由外往内传递时)响应事件。false 表示该元素在事件的“冒泡阶段”(由内向外传递时)响应事件
vue 的事件代理:v-for 遍历数组,得设置 data-index=“index”,然后通过 e.target.dataset.index 才知道某个子元素的下标值。
9.说说 stopPropagation preventDefault 的区别
1.stopPropagation 阻止默认冒泡。e.stopPropagation();
2. PrenventDefault 阻止默认事件,如点击 a 标签默认跳转。e.preventDefault();
10.如何设置 localStorage 存活一定时间
存储 localStorage 的值时加上一个时间戳,当下次取值的时候验证时间戳是否过期。
https://blog.csdn.net/qq_20343517/article/details/83656450
11.如何实现人为垃圾回收 强制 delete 设置为 null
// 。。。。可能先删除自己且又删除原型链上的
12.如何字符串转数值型
若 str = ‘123’
- parseInt(str) 2. parseFloat(str)3.Number(str)4. +str
13.如何判断一串字符串完全是由数字组成的字符串
1. for循环判断每一个字符。2.正则表达式 `\^\d+$\.test('123a')`
14. 说一说 DOM 和 BOM
DOM 文档对象模型;BOM 浏览器对象模型;
BOM 常见对象:window,document,location,screen,history
BOM 常见方法:Alert(),confirm(),open(),close(),prompt()
15.typeof 箭头函数结果是什么?如何判断一个变量是普通还是箭头函数?
typeof 箭头函数结果为 function 。
16.call 、apply 、 bind
都是用来改变执行上下文,this 指向的。
区别:1. call、apply 改变执行的同时也执行函数,而 bind 改变执行后是返回一个函数,需要变量接收再执行。2. call、和 bind 的参数以 ,逗号分隔。apply 参数为一个数组形式。
17.js 继承有哪些 :
原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承、class 继承
function Animal(name) {
this.name = name;
this.arr[(1, 2, 3)];
}
Animal.prototype.sayName = function () {
console.log(this.name);
};
function Dog() {
}
1.原型链继承(子类的原型为父类的实例):
Dog.prototype = new Animal();
var dog = new Dog();
优点:
1、实例可继承的属性有:实例的构造函数的属性,父类构造函 数属性,父类原型的属性。(新实例不会继承父类实例的 属性!)
缺点:1、新实例无法向父类构造函数传参。
2、继承单一。
3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
2.借用构造函数继承:用.call()和.apply()将父类构造函数引入子类函数(在子类 函数中做了父类函数的自执行(复制)):
function Dog(name){ Animal.call(this,name) }
优点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、解决了原型链继承缺点1、2、3。
3、可以继承多个构造函数属性(call多个)。
4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。没有继承父类原型的方法。
2、无法实现构造函数的复用。(每次用每次都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。
3.组合继承(组合原型链继承和借用构造函数继承)
function Dog(name){
Animal.call(this,name)
}
Dog.prototype = new Dog();
优点:1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
4.原型式继承(类似object.create()的原理,创建一个构造函数,构造函数的原型指向对象,然后调用 new 操作符创建实例,并返回这个实例):
function Dog(obj){
var temp = function(){
};
temp.prototype = obj;
return new temp();
}
var dog = new Animal();
var jinmao = Dog(dog);
缺点:无法向父类构造函数传参。如果父类是构造函数,无法继承父类构造函数中的属性,只能继承父类构造函数的原型对象的属性。如果父类是普通对象,引入类型属性依然被子例共享
5.寄生式继承(原型式继承外面套了个壳子。好传递参数。):
function Dog(obj){
var temp = function(){
};
temp.prototype = obj;
return new temp();
}
var dog = new Animal();
function Dog2(dog){
var dog2 = Dog(dog);
dog2.name = 'jingmao';
return dog2;
}
var jingmao = Dog2(dog)
缺点:没用到原型,无法复用。
6.寄生组合继承(重点应该是本来是直接new一份,new出来的默认有指向父类。现在是新建一个空对象指向父类就行。这样就解决了2份相同属性问题):
function Dog(name){
Animal.call(this,name)
}
if(!Object.create){
Object.create = function(proto){
function Temp(){};
Temp.prototype = proto;
return Temp();
}
}
Dog.prototype = Object.create(Animal.prototype)
重点:修复了组合继承的问题。
也可以用 Object.setPrototypeOf() 指定它直接指向父类原型。
7.class继承 class本质也是函数
class Animal {
constructor(name){
this.name = name;
}
sayName(){
console.log('name')
}
}
class Dog extend Animal{
constructor(name,age){
super(name);
this.age = age;
}
sayName(){
super.sayName();
}
}
var haSiQi = new Dog('haSiQi','1');
18. 常用操作节点方法:
nodeType 返回节点类型的值:
节点类型 | 值 | 对应常量 |
---|---|---|
文档节点(document) | 9 | Node.DOCUMENT_NODE |
元素节点(element) | 1 | Node.ELEMENT_NODE |
属性节点(attr) | 2 | Node.ATTRIBUTE_NODE |
文本节点(text) | 3 | Node.TEXT_NODE |
文档类型节点(DocumentType) | 10 | Node.DOCUMENT_TYPE_NODE |
注释节点(Comment) | Node.COMMENT_NODE | |
文档片断节点(DocumentFragment) | 11 | Node.DOCUMENT_FRAGMENT_NODE |
var div = document.createElement(‘div’)
1. body.appendChild() 追加节点
2. body.removeChild() 要移除的节点
3. body.insertBefore()在参照节点前插入节点 第一个参数要插入的节点 第二个参数参照节点
4.var clone = div.cloneNode(true)复制节点 有参数true代表深复制
5. body.replaceChild()第一个参数:要插入的节点;第二个参数:要替换的节点;
19.new Date()方法:
var myDate = new Date();
myDate.getYear(); //获取当前年份(2位)
myDate.getFullYear(); //获取完整的年份(4位,1970-????)
myDate.getMonth(); //获取当前月份(0-11,0代表1月) 所以获取当前月份是 myDate.getMonth()+1;
myDate.getDate(); //获取当前日(1-31)
myDate.getDay(); //获取当前星期X(0-6,0代表星期天)
myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数)
myDate.getHours(); //获取当前小时数(0-23)
myDate.getMinutes(); //获取当前分钟数(0-59)
myDate.getSeconds(); //获取当前秒数(0-59)
myDate.getMilliseconds(); //获取当前毫秒数(0-999)
myDate.toLocaleDateString(); //获取当前日期
var mytime = myDate.toLocaleTimeString(); //获取当前时间
myDate.toLocaleString( ); //获取日期与时间
//------------------------------------------------
yyyy: 年份,2014
yy: 年份,14
MM: 月份,补满两位,09
M: 月份, 9
dd: 日期,补满两位,05
d: 日期, 5
HH: 24制小时,补满两位,13
H: 24制小时,13
hh: 12制小时,补满两位,01
h: 12制小时,1
mm: 分钟,补满两位,14
m: 分钟,14
ss: 秒,补满两位,20
s: 秒,20
w: 星期,为 ['日', '一', '二', '三', '四', '五', '六'] 中的某一个,本 demo 结果为 五
//-----------------------------------------------------
/* 第一个参数为你的时间戳,第二个参数为想要的时间格式 */
function formatDate(date, format) {
date = new Date(date);
function addZero(date) {
if (date < 10) {
return "0" + date;
}
return date;
}
let getTime = {
yyyy: date.getFullYear(),
yy: date.getFullYear() % 100,
MM: addZero(date.getMonth() + 1),
M: date.getMonth() + 1,
dd: addZero(date.getDate()),
d: date.getDate(),
HH: addZero(date.getHours()),
H: date.getHours(),
// 对于要求12小时制的可以直接 %12
hh: addZero(date.getHours() % 12),
h: date.getHours() % 12,
mm: addZero(date.getMinutes()),
m: date.getMinutes(),
ss: addZero(date.getSeconds()),
s: date.getSeconds(),
// 返回星期几,立即执行函数
w: (function () {
let a = ["日", "一", "二", "三", "四", "五", "六"];
return a[date.getDay()];
})(),
};
for (let i in getTime) {
format = format.replace(i, getTime[i]);
}
return format;
}
console.log(formatDate(1409894060000, "yy-M-d hh:m:s 星期w"));
20.深浅拷贝:
浅拷贝:
1;
for (var i in obj) {
newObj[i] = obj[i];
}
2;
for (var i of Object.keys(obj)) {
newObj[i] = obj[i];
}
深拷贝:
1.
2.-------------------------------------------
var newObj = JSON.parse( JSON.stringify( oldObj ) ) ;
其中存在属性值为undefined会忽略该属性
21.数组去重方法:
- set
var newArr = [...new Set(oldArr)];
- filter + indexOf
var newArr = oldArr.filter((item, index, arr) => {
return arr.indexOf(item, 0) == index;
});
- includes includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则 false。
for (var i = 0; i < oldArr.length; i++) {
if (!newArr.includes(oldArr[i])) {
newArr.push(oldArr[i]);
}
}
22.快速打乱一个数组排序:
arr.sort(() => {
return Math.random() - 0.5;
});
23. addEventListener() 与 attachEvent()区别:
-
addEventListener()是符合 W3C 规范的标准方法; attachEvent()是 IE 低版本的非标准方法
-
addEventListener()支持事件冒泡和事件捕获; - 而 attachEvent()只支持事件冒泡
-
addEventListener()的第一个参数中,事件类型不需要添加 on; attachEvent()需要添加’on’
-
如果为同一个元素绑定多个事件, addEventListener()会按照事件绑定的顺序依次执行, attachEvent()会按照事件绑定的顺序倒序执行
24.已知有字符串 foo=“get-element-by-id”,写一个 function 将其转化成驼 峰表示法"getElementById"
function combo(msg) {
var arr = msg.split("-");
var len = arr.length;
for (var i = 1; i < len; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1);
}
return arr.join("");
}
25.JavaScript 模块化规范:
以前浏览器常用的模块化规范有 AMD(代表 Require.js)和 CMD(代表 Sea.js),但是都落伍了。服务端有 CommonJS 规范。不过也不是标准的。最终 ES6 官方提出了大一统的模块化规范,这也是目前浏览器与服务端的通用规范。在 node 因为 es6 模块出得比较晚,所以 commonJS 还有人用。
commonJS( module.exports 导出,require 导入 ):
//导出
var x = 1;
function add() {
}
module.exports.x = x;
module.exports.add = add;
//导入
var obj = require("./xxx");
obj.x;
obj.add();
ES6 https://auroras.blog.csdn.net/article/details/120400803:
//导出
export function add(a, b) {
console.log(a + b);
}
//导入
import {
add } from "./xxx";
commonJS 与 es6 区别:
CommonJS 模块输出的是⼀个值的拷⻉,ES6 模块输出的是值的引⽤。
CommonJS 模块是运⾏时加载,ES6 模块是编译时输出接⼝。
CommonJS 模块的 require() 是同步加载模块,ES6 模块的 import 命令是异步加载,有⼀个独⽴的模块依赖的解析阶段。
26.拖拽
在 H5 中实现了拖拽技术,允许用户在网页内部拖拽以及浏览器与其他应用程序之间的拖拽,通过拖拽可以传递数据。
元素取消默认的禁止拖动:<div draggable="true"></div>
拖动事件:dragstart, drag,dragend
放置事件: dragenter,dragover, drop
拖拽事件流:
当拖动一个元素放置到目标元素上的时候将会按照如下顺序依次触发
dragstart->drag->dragenter->dragover->drop->dragend
-----------------------------------------------------------------------
2.在拖拽事件中,我们可以通过 dataTransfer 来实现数据交互,通过 event.dataTransfer 来获取 dataTransfer 实例
方法:setData(),getData,clearData
26. proxy 构造器代理:
其实就是一个 es6 新提供的 API,当你对象或者函数调用一些方法的时候它能拦截到,并且你可以自定义它的返回。
https://blog.csdn.net/yangyang_A/article/details/106146447
- get 读取属性,set 设置属性值,类似于 Object.defineProperty 的 set 和 get:
let obj = {
name: "terry",
age: 12,
};
let proxy = new Proxy(obj, {
//target拦截的目标对象,key属性,value属性值
set(target, key, value) {
target[key] = value;
},
get(target, key) {
return target[key];
},
});
proxy.age = 33;
console.log(obj);
apply
方法可以拦截函数的调用call
和apply
时的操作。
function add(a, b) {
return a + b;
}
let proxy = new Proxy(add, {
//target目标函数,that改变的this指向,args参数数组
apply(target, that, args) {
console.log(target + "---" + that + "---" + args);
},
});
proxy.apply(null, [2, 3]);
3.construct(),拦截new
一个函数或者类命令,construct
方法返回的必须是一个对象,否则会报错。
function Animal(name) {
this.name = name;
}
let p = new Proxy(Animal, {
construct(target, args) {
return new target(...args);
},
});
console.log(new p("jinmao"));
27.反射 reflect:
Reflect 是⼀个内置的对象,它提供拦截 JavaScript 操作的⽅法。这些⽅法与 proxy 的⽅法 相同。 Reflect 不是⼀个函数对象,因此它是不可构造的。类似于 proxy 里的 target。
//reflect.apply(target,thisArg,args):
function foo(name) {
console.log(name);
}
let proxy = new Proxy(foo, {
apply(target, that, args) {
Reflect.apply(target, that, args);
},
});
proxy("123");
28.谈一谈 this:
this 的指向不是在函数声明时确定的,而是在调用执行时确定的,同时,this 不同的指向在于遵循了一定的规则。
1.默认情况下,指向全局,浏览器的话就是指向 window。
2.如果函数被调用的位置存在上下文,那么函数被隐式绑定。
3.如果存在构造函数,this 指向 new 的这个新对象。
4.箭头函数的是没有属于自己的 this 的,它会继承父作用域的 this,所以它是在声明时确定的。同时,它不可以 new,也没有 arguments,也不能用 call、bind 改变。
5.call,apply 能改变 this 的指向。
29.js 原型作用:
封装一些处理问题的方法,可以实现继承,减少冗余代码,高度复用,简化代码,减少空间占用。构造函数的所有实例都可以访问构造函数原型中的方法和属性。也就是把共有的东西统一存放到一个共享空间。这样可以避免相同函数重复声明,减少空间占用,节省内存。
30.什么是 js 垃圾回收机制不能回收的(内存泄露):
1:意外的全局变量 ,都指向了 window
function foo(arg) {
bar = "this is a hidden global variable";
}
或者:
function foo2(){
this.varable = "hello"
}
foo2();//调用后,this指向window全局变量
2:被遗忘的定时器或回调函数
3:脱离 DOM 的引用
4:闭包,内部函数引用外部函数变量,得不到释放
三. 计算机网络
1.说说浏览器渲染的基本过程
1. 根据HTML结构生成DOM树。
2. 根据css生成CSSOM树。
3. 将DOM树与CSSOM树整合成RenderTree渲染树。
4. 根据RenderTree开始渲染和显示。
5. 期间遇到`<JavaScript>`会执行并阻碍渲染。
2.说说三次握手四次挥手的过程
- 三次握手是客户端与服务端建立 TCP 连接时要发送 3 个包来建立连接。第一次握手,客户端发送SYN 包到服务器,等待服务器确认,并进入SYN_SENT 状态。第二次握手,服务器收到 SYN 包,确认,同时自己也发送一个 SYN 包(syn=k),即SYN+ACK 包,此时服务器进入SYN_RECV 状态;第三次握手,户端收到服务器的 SYN + ACK 包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP 连接成功),完成 3 次握手。
- 四次挥手是客户端与服务端终止 TCP 连接时要发送四个包确认断开。第一次挥手,服务端申请断开连接。第二次挥手,客户端收到断开消息并表示知道了。第三次挥手,客户端确认可以断开了。第四次挥手,服务端接受信息,返回数据表示已接受信息。
3.说说 http 的两大存储
http 两大存储是 强缓存 和 协商缓存。
- 强缓存。当缓存数据未失效情况下,直接使用浏览器数据缓存数据,不会向服务器发送任何请求。
- 协商缓存。由服务器来确定缓存资源是否可用,客户端与服务端通过某种标识来进行通信,从而让服务器判断请求资源是否可用。如果可用,返回 304 状态码,客户端调用缓存内容。
4.http 和 websocket 有什么区别
相同点:
1,都是应用层的协议。
2,都是基于 tcp,并且都是可靠的协议。
不同点:
- websocket 是持久连接协议,http 是非持久连接协议。
- websocket 是双向通信协议,可以双向发送消息,而 http 是单向的。
- websocket 的服务端可以主动向客户端发送数据,而 http 服务端只有在客户端发起请求时才能发送数据,不能主动。
5.http 和 https 的区别
- http 明文传输,不安全。https 可加密、身份认证的网络协议,安全。
- https 需要 CA 证书,要花钱。
- http 响应更快,因为建立 TCP 连接需要交换 3 个包。而 https 需要 12 个包。
- 端口不一样。http 是 80 端口,https 是 443 端口。
6.https 如何实现加密的:
对称加密+非对称加密+CA 机构数字证书+数字签名
1、服务器在给客户端传送公钥的过程中,会把公钥以及服务器的个人信息通过哈希算法生成信息摘要。
2、之后服务器利用 CA 提供的私钥对信息摘要进行加密,来形成数字签名。
3、将没有经过哈希算法处理的个人信息以及公钥,和数字签名合并在一起,形成数字证书。
7.状态码 304 是什么意思
如果客户端发送了一个请求且被允许,但是文档内容(自上一次访问以来或者根据请求条件)没有变化,那么返回 304 状态码,客户端调用缓存内容,不必进行二次调用。(这应该就是协商缓存)
8.简单说一说 http:
一种处于应用层的超文本传输协议。是一种客户端和服务端数据交互的一种规则。HTTP 报文分为请求报文和响应报文。请求报文:请求行、请求头、空行、请求体。响应报文:状态行、响应头、空行、响应体。
9.get 与 post 请求区别:
- get 请求从服务器获取资源,post 请求向服务器提交数据。
- get 提交数据暴露在地址栏,post 数据放在请求体。
- get 传输数据有限,受到 URL 长度限制。而 post 可以传输大量的数据。
- POST 的安全性比 GET 的高。
10. 常见 http 状态消息含义
200 | 请求成功 |
---|---|
301 | 请求资源已经永久重定向,自动重定向到新 url |
302 | 请求资源临时重定向,应继续使用原 url |
304 | 请求资源未修改,使用本地存储资源即可 |
400 | 客户端请求语法错误,服务端无法理解 |
401 | 未经授权:访问由于凭据无效被拒绝。 |
403 | 服务端拒接执行请求 |
404 | 找不到资源 |
500 | 服务器内部错误 |
503 | 超载或者系统维护,无法处理请求 |
504 | 网关超时,服务器作为网关或代理,但没有及时从上游服务器获取请求 |
505 | 服务器不支持请求的 HTTP 协议版本 |
11. cookie 与 session:
(1)cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
(2)cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗,如果主要考虑到安全应当使用 session。
(3)session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用 COOKIE。
(4)单个 cookie 在客户端的限制是 3K,就是说一个站点在客户端存放的 COOKIE 不能 3K。
(5)所以:将登陆信息等重要信息存放为 SESSION;其他信息如果需要保留,可以放在 COOKIE 中。
12.cookie、sessionStorage、localStorage 区别:
共同点:都保存在浏览器端、且同源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0DRcBHoc-1657174227829)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210830161621364.png)]
不同点:
- cookie 在 http 请求中携带并在浏览器和服务器间传递,sessionStorage、LocalStorage 仅在浏览器本地保存。
- 存储大小限制不同,cookie 数据不能超过 4k,因为是携带在 http 请求中所以只适合很小的数据。sessionStorage 和 localStorage 就大许多,可以达到 5M 或更大。
- 数据有效期不同。cookie 在设置过期时间后或默认关闭浏览器后失效。sessionStorage 当浏览器窗口关闭后失效。localStorage 除非手动删除不然永久保存。
- 作用域不同。
用法:
cookies:
1.下个 js-cooki 插件:
//创建一个cookie,7天后过期 :
Cookies.set('token','123',{
expires:7})
//获取cookie
Cookies.get('token');
//移除cookie
Cookies.remove('name');
sessionStorage:
// 设置内容
sessionStorage.setItem("token", "123");
// 获取内容
sessionStorage.getItem("token");
// 清空指定键内容
sessionStorage.removeItem("token");
// 清空All内容
sessionStorage.clear();
localStorage:
// 设置内容
localStorage.setItem("token", "123");
// 获取内容
localStorage.getItem("token");
// 清空指定键内容
localStorage.removeItem("token");
// 清空All内容
localStorage.clear();
13. 跨域与同源策略:
跨域指一个域下的文档或者脚本试图去请求另一个域下的资源,这里跨域是广义的。
同源策略指浏览器的一种安全机制,就是‘协议+域名+端口’三者要全部相同。同源会限制以下行为:Cookie、LocalStorage 和 IndexDB 无法读取;DOM 和 JS 对象无法获得;AJAX 请求不能发送。
解决跨域方案:
- jsonp ,缺点只能处理 get 请求,原理就是 script 标签里的 src 写跨域地址::
<script>
var script = document.createElement("script");
script.type = "text/javascript";
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = "http://123.123:8080?user=admin&callback=handleCallback";
document.head.appendChild(script);
// 执行回调函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
服务端返回(返回时即执行全局函数):
handleCallback({'success':true,"user":'admin'})
2.服务器代理:
根目录创建 vue.config.js,配置 proxy,就是让与自己在同一域下的本地服务器去发送请求,因为服务器与服务器间没有跨域,跨域是浏览器的同源安全机制产生的。所以,本地服务器去发送请求获得数据,我们在从本地服务器获得数据就不会跨域了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StlKNOG0-1657174227830)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210821165029984.png)]
14.异步执行机制【事件循环机制 EventLoop】,宏任务与微任务:
- 所有同步任务都在主线程上执行,形成一个执行栈。 2. 主线程之外,还存在一个"任务队列"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 4. 主线程不断重复上面的第三步。
简单总结事件循环机制:
同步代码执行 -> 查找异步队列,推入执行栈,执行 Vue.nextTick[事件循环 1] -> 查找异步队列,推入执行栈,执行 Vue.nextTick[事件循环 2]…
任务队列中的任务会被分为两种:微任务(microtask)和宏任务(macrotask)。
· 宏任务包括 script 脚本, setTimeout ,setInterval
· 微任务包括 Promise
- 首先执行同步代码,即 script 脚本 2. 当执行完所有同步代码后,执行栈清空 3. 从微任务队列中逐个取出回调任务,放入执行栈中执行,直至所有微任务执行完成。注意:如果在执行微任务的过程中,产生了新的微任务,那么这个微任务会加入到队列的末尾,同样会在这个周期内被执行。 4. 当执行完所有微任务后,如果有必要会开始渲染页面 5. 开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数。
1)、存在微任务的话,那么就执行所有的微任务
2)、微任务都执行完之后,执行下一个宏任务
3)、1, 2 以此循环着
15.offsetWidth、offsetHeight,clientWidth、clientHeight,scrollwidth,scrollHeight 区别:
- offsetWidth/offsetHeight 返回值包含content + padding + border,效果与 e.getBoundingClientRect()相同
- clientWidth/clientHeight 返回值只包含content + padding,如果有滚动条,也不包含滚动条
- scrollWidth/scrollHeight 返回值包含content + padding + 溢出内容的尺寸
16. 服务端渲染与客户端渲染区别(SPA SEO SSR):
-
服务端渲染:在早期的时候,由于页面比较简单,前后端分离还没有做的比较完善,所以当时一般页面渲染还是在服务端完成 html 文件的拼装,然后浏览器接收到这个文件,就可以直接解析 展示。
-
客户端渲染:如今前端页面的复杂性提高,前端已经不再仅仅是普通的页面展示了,现在前端页面功能更加完善,也更加复杂。同时伴随着 ajax 的兴起,使得现在越来越崇尚前后端分离的开发方式。后端不再提供完整的 html 页面,而是提供一些 API 使得前端可以获取需要的 json 数据,然后前端拿到数据后在前端完成 html 页面的拼装,然后展示在浏览器上,这就是客户端渲染。
-
SEO:搜索引擎优化。利用搜索引擎的规则提高网站在有关搜索引擎的自然排名。
-
SPA(客户端渲染)。单页面应用:只有一张 Web 页面的应用,是一种从 Web 服务器加载的富客户端,单页面跳转仅刷新局部资源,公共资源仅需加载一次,常用于 PC 端网站,购物等网站。优点:页面之间切换快,减少了服务器的压力。缺点:首屏打开慢,不利于 SEO 搜索引擎优化,利用搜索引擎的规则是提高网站在有关搜索引擎的自然排名。
-
SSR(服务端渲染)。
又称为后端渲染,服务器端在返回 html 之前,在 html 特定的区域特定的符号里用数据填充,再给客户端,客户端只负责解析 HTML。
SR 的出现一定程度上解决了 SPA 首屏慢的问题,又极大的减少了 SPA 对于 SEO 的不利影响。优点:更快的响应时间,不用等待所有的 js 都下载完成,显示器便能显示出比较完整的页面;更好的 SSR,我们可以将 SEO 关键信息直接在后台渲染成 html,从而保证了搜索引擎可以提取到相应数据。缺点:占用了大量的 CPU 和内存资源。
1.注重 SEO 的新闻网站,非强交互的页面,建议采用服务器端渲染 2.对于强交互的页面,不注重 SEO,采用客户端渲染 3.只需改善少数页面的 SEO,采用预渲染
17.DNS 解析:
18.TCP 与 UDP 区别:
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅 8 字节 | 首部最小 20 字节,最大 60 字节 |
适用场景 | 适用于实时应用(IP 电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
首先 UDP 是不需要和 TCP 一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。
19.谈谈 window.history:
window.history 方法提供了对浏览器历史记录的读取,让你可以在用户的访问记录中前进和后退。
History 对象属性:
length:返回浏览器历史列表中的 URL 数量。
History 对象方法:
back():加载 history 列表中的前一个 URL。即后退
forward():加载 history 列表中的下一个 URL。即前进
go():加载 history 列表中的某个具体页面。这个是最常用的
20.谈谈 webSocket:
- websocket 是 HTML5 提供的一种在单个 TCP 链接上进行全双工通信的协议。
- 使用 websocket 可以在服务器与客户端之间建立一个非 HTTP 的双向连接,允许服务端主动向客户端推送数据,连接是实时与永久的,除非被显示关闭。
21. http 协议中与资源缓存相关的协议头有哪些?
cache-control、pragma、expires
22.http 各个版本对比:
HTTP/0.9:功能捡漏,只支持 GET 方法,只能发送 HTML 格式字符串。
HTTP/1.0:支持多种数据格式,增加 POST、HEAD 等方法,增加头信息,每次只能发送一个请求(无持久连接)
HTTP/1.1:默认持久连接、请求管道化、增加缓存处理、增加 Host 字段、支持断点传输分块传输等。
HTTP/2.0:二进制分帧、多路复用、头部压缩、服务器推送
HTTP/0.9 | 最早发现的一个版本,在 1991 年发布,只接受 GET 一种请求方法,并且不支持请求头。 |
HTTP/1.0 | 1.0 版本的工作方式是每次 TCP 连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立 TCP 连接,就是不支持 keep-alive。 TCP 连接的建立成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。增加了请求方式 POST 和 HEAD |
HTTP/1.1 | **引入了持久连接(**persistent connection),即 TCP 连接默认不关闭,可以被多个请求复用,不用声明 Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送 Connection: close,明确要求服务器关闭 TCP 连接。 |
HTTP/2.0 | 增加双工模式,即不仅客户端能够同时发送多个请求,服务端也能同时处理多个请求,解决了队头堵塞的问题(HTTP2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP1.1 大了好几个数量级); |
23.浏览器输入 url 后经历的过程:
- 输入 url
- 判断浏览器缓存是否有数据,有则调用显示,无则跳过
- DNS 域名解析,获取 ip 地址
- 浏览器发起 tcp 连接 , 三次握手建立连接
- 浏览器发送 http 请求获取数据
- 服务器收到请求返回数据
- 浏览器得到 http 响应得到数据
- 浏览器读取数据,构建 dom 树,cssom 树,渲染树,js
- 客户端与服务端的一些交互
- ajax 查询
24.token 的作用:
客户端使用用户名跟密码请求登录
服务端收到请求,去验证用户名与密码
验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
1、Token 的引入:
Token 是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token 便应运而生。
2、Token 的定义:
Token 是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个 Token 便将此 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次带上用户名和密码。
3、使用 Token 的目的:
Token 的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
4.Token 的优点:
扩展性更强,也更安全点,非常适合用在 Web 应用或者移动应用上。Token 的中文有人翻译成 “令牌”,我觉得挺好,意思就是,你拿着这个令牌,才能过一些关卡。
5.Token 一般用在三个地方:
① 防止表单重复提交
②anti csrf 攻击(跨站点请求伪造)
③ 身份验证(单点登录)
25.常见的宏任务和微任务:
宏任务(macrotasks): 主 js、UI 渲染、setTimeout、setInterval、setImmediately、requestAnimationFrame、I/O 等
微任务(microtasks):process.nextTick()、promise.then()(new Promise 不算!)、Object.observe()等;
四. vue的基础得会
1.说说你用过组件通信的方式
- 父向子组件传值,属性绑定,props 接收。
- 子向父传值,$emit 传递,事件绑定接收。
- 兄弟传值。Vue.prototype. b u s = n e w V u e ( ) , v u e 原型上新建 v u e 实例, bus = new Vue(),vue原型上新建vue实例, bus=newVue(),vue原型上新建vue实例,emit 传递,$on 接收。
- 后代传值。定义 provide(){return{xxx:this}}发送,inject:[‘xxx’]接收。
- vuex 状态机。包括 state、mutations、actions、getter、modules。
- 路由编程式导航 push 传值。
2.vue 里面有个虚拟 dom,你了解吗
1.首先,如果 DOM 树结构改变直接全部覆盖的话会造成许多不必要的浪费,因为可能只是改变 DOM 结构里的一点点。
2.虚拟 DOM 就是为了将虚拟结点渲染到视图上以免造成浪费。在一个组件实例第一次渲染的时候,构建 DOM 树的同时构建一个虚拟 DOM。以后每次改变都用虚拟节点 vnode 和旧虚拟节点 oldVnode 进行对比,vue 会找到最小更新量,只更新改变部分的视图。
3.用过 nextTick 吗,原理是什么
this.$nextTick(function(){ })是 vue 提供的一个 api,当一次 dom 循环更新完毕之后才延迟执行的一个回调函数。目的是为了节省性能。
vue 更新是异步执行的,当观察到数据变化,vue 将开启一个队列,等到同一时间循环中所有数据变化完成后,才会统一进行视图更新。而为了确保得到的是更新之后的 DOM,所以设置了 vue.nextTick()方法。应用场景:需要在视图更新之后,基于新的视图进行操作。
也可以 this.$nextTick(()=>{ }) ;
4.为什么 data 是个函数
避免组件复用产生的影响。当同一个组件被多次复用的时候会创建多个实例。如果 data 是一个对象的话,实例的数据可能指向同一片内存空间,造成混乱。所以为了保证数据独立,通过函数返回一个对象形成独立作用域。
5.v-for 循环中的 key 是干嘛的
我认为是指定一个唯一标识符。更好操作某个特定的元素。
官方解释: key
的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
6.了解 keep-alive 吗,说说
keep-alive 是 vue 提供的一个组件。它能够缓存不活动的组件的状态。避免多次重复渲染消耗性能。用 <keep-alive></keep-alive>
包裹要缓存的组件。也可以直接包裹<router-view>
,相当于路由里的组件都缓存。
可以设置 include 属性,只有名称匹配的组件才缓解。
可以设置 exclude 属性,只有名称匹配的组件不缓解。
可以设置 max 属性,最多缓存几个组件。
可以在路由设置,meta:{keepAlive:true} ,然后在 router-view 定义 v-if=“$router.meta.keepAlive”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WlhXOM1u-1657174227832)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210823202909046.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0c9WTzBa-1657174227833)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210823203209608.png)]
7.八大生命周期:
beforeCreate:vue 实例初始化之前。
// 数据劫持
created:vue 实例初始化之后。
// 数据双向绑定完成
beforeMount:Dom 树挂载之前。
mounted:dom 树挂载之后。(ajax 异步操作)
beforeUpdate:数据更新之前。
updated:数据更新之后。
beforeDestroy:vue 实例销毁之前。(释放资源)
destroyed:vue 实例销毁之后。
8.watch 与 computed 区别:
- 计算属性 computed 支持缓存,只有当依赖数据发送改变才重新计算。
- watch 不支持缓存,只要数据改变,就直接触发相应的操作。
- computed 不支持异步操作,当 computed 内有异步操作将无法执行。
- watch 支持异步操作。
data(){
return:{
num:1,
message:'hello',
obj:{
sum:123
}
}
},
computed:{
gai(){
return this.message.split('').reverse().join('')
}
},
watch:{
num(newVal,oldVal){
this.obj.sum = 456;
},
obj:{
sum(newVal,oldVal){
this.num = 10;
},
deep:true
}
}
immediate:immediate 属性设置为 true 后页面刷新就立刻监听。
计算属性不一定需要返回结果。
9.说一下 v-on
v-on 是绑定事件的,可以简写为@
10.说一说单向数据流与双向数据流:
首先,vue 是单向数据流且支持单向或双向数据绑定。数据流指的是组件之间的数据流动。
11. 说一说 vue 插槽:
父组件可以向子组件传递内容,同时扩展、复用、定制组件。
1.基本用法 , 默认插槽,很简单,就是如果我在子组件的< slot >< slot >插槽可以定义一些默认内容,如果父组件引入子组件然后什么都没写,那么显示默认值,如果父组件那写了,覆盖掉默认值:
// father:
<Children>
<h1> 父组件书写的内容 </h1>
</Children>
// child:
<slot>插槽默认内容</slot>
2.具名插槽,顾名思义,就是给插槽起名。当有多个插槽时,肯定得定义名字来区分,看看你想显示什么内容。:
// child:
<slot name="one"></slot>
// father:
<Children>
<template v-slot:one>
<h1> 父组件书写的内容 </h1>
</template>
</Children>
3.作用域插槽,简单来说就是子组件作用域的数据能传递给父组件,并且在父组件的引入的子组件的标签里可以引用,那么它也就可以覆盖掉子组件里插槽的默认值。:
// child:
<slot v-bind:giveBlog="blog"></slot>
...data(){ return {blog:"night"} } // father:
<Children>
<tmplate v-slot:default="getBlog">
{
{getBlog.giveBlog}}
</tmplate>
</Children>
12. 说一说导航守卫:
包括全局前置守卫、路由守卫、组件守卫
参数 | 意义 |
---|---|
to | 代表将要跳到哪个路由 |
from | 代表之前是来自哪个路由 |
next() | 进行正常跳转 |
next(false) | 取消跳转 |
next(“路径”)或者 next({ path: ‘路径’ }) | 路由重定向,跳转到其它页面 |
- 全局前置守卫,在 main.js 书写:
router.beforeEach((to,from,next)=>{
next();
})
- 路由守卫,只有进入到当前路由页面的时候才会执行的守卫函数:
...
{
path:'/user',
component:User,
beforeEnter:(to,from,next)=>{
...
}
}
...
3.组件守卫,它们也是函数,跟生命周期钩子函数有点像,跟 data()、created()这些函数也是同一个级别的。它既然是写在组件里的,那说明它也是在该组件中有效的。:
进入组件之前调用 beforRouteEnter:
beforeRouteEnter(to,from,next){
...
}
离开组件时调用 beforeRouteLeave:
beforeRouterLeave(to,from,next){
const r = window.confirm("那你走?输入信息将不会保留");
if (r) {
next();
} else {
next(false);
}
}
组件复用调用,当连续进入同一个动态路由页面时会产生复用。beforeRouteUpdate:
beforeRouteUpdate(to,from,next){
...
}
13.说一说路由:
基本使用:
<router-link to="/page1"> </router-link>
...
<router-view></router-view>
... const router = new VueRouter({ routes:[ {path:'/page1',component:Page1} ] })
嵌套路由, children:
{
path:'/page2',
component:Page2,
children:[
{
path: '/page2/star',component:Star},
{
path: '/page2/moon',component:Moon}
]
}
动态路由,数据不同,但都是同一个组件,组件复用:
{
path:'/page/1',component:Page},
{
path:'/page/2',component:Page},
简化为:
{
path:'/page/:id',component:Page }
路由跳转传参数 params(地址不显示)和 query(地址显示):
this.$router.push({path:‘/user’,query:{p: this.id }})
接收: this.$route.query.id
14. 路由懒加载:
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
1. es6
{
path:'/304',
name:'304',
component:()=> import(@/123/304)
}
2.--------------- vue异步组件
{
path: '/304',
name:'304',
component:()=> resolve =>require(['@/123/304'])
}
3.--------------- webpack
{
path: '/304',
name:'304',
component:()=> r => require.ensure([],()=>r(require('@/123/304')),'304')
}
15.双向数据绑定原理:
在 vue2 中使用 es5 的 Object.defineProperty: Object.defineProperty(obj,prop,descriptor)能监听对象属性变化,当 vue 数据改变,defineProperty 能数据劫持,监听到并重新更新渲染相关 DOM 元素。底层利用发布者,订阅者模式。
在 vue3 中使用 es6 的 proxy 代理。proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Object.defineProperty(obj,'weight',{
configurable:true,
enumerable:true,
writable:true,
value:'123'
})
// 监听age
var obj = {
age: 1,
_age: 1,
};
Object.defineProperty(obj, "age", {
set(newVal) {
this._age = newVal;
},
get() {
return this._age;
},
});
configurable |
enumerable |
value |
writable |
get |
set |
|
---|---|---|---|---|---|---|
数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
16.template 与 render 函数的区别:
17.异步组件加载:
18.函数式组件:
19. query 与 params 区别
1.都是编程式导航时传参数。传参可以使用 params 和 query 两种方式。 2.使用 params 传参只能用 name 来引入路由,即 push 里面只能是 name:’xxxx’,不能是 path:’/xxx’,因为 params 只能用 name 来引入路由,如果这里写成了 path,接收参数页面会是 undefined!!!。 3.使用 query 传参使用 path 来引入路由。
4.params 是路由的一部分,必须要在路由后面添加参数名。query 是拼接在 url 后面的参数,没有也没关系。 5.二者还有点区别,直白的来说 query 相当于 get 请求,页面跳转的时候,可以在地址栏看到请求参数,而 params 相当于 post 请求,参数不会再地址栏中显示。
query:
this.$router.push({
path: "/index",
query: {
id: 123 },
});
//---
this.id = this.$route.query.id;
params:
this.$router.push({
name: "index",
params: {
id: 123 },
});
//------
this.id = this.$route.params.id;
20.vue 组件化构建:
- 创建组件构造器 Vue.extend()
- 注册组件 Vue.component()
- 使用
全局注册(不需要引入可直接使用):
// 1. 创建一个组件构造器
let myButton = Vue.extend({
// 模板选项
template: `<button>点击我</button>`,
});
// 2. 注册组件,并指定组件的标签,组件的HTML标签为<my-button>
Vue.component("my-button", myButton); // 组件名 构造器
//语法糖简写
Vue.component("my-button", {
//选项对象
template: `<button>点击我</button>`,
});
局部注册():
let myButton = Vue.extend({
// 模板选项
template: `<button>点击我</button>`
})
new Vue({
components:{
'my-button':myButton
}
})
//语法糖简写
new Vue({
el:'#app',
components:{
'my-button':{
`<button>点击我</button>`
}
}
})
动态组件:
我们之前在一个多标签的界面中使用 is
attribute 来切换不同的组件:
<component v-bind:is="currentTabComponent"></component>
21.vue 事件修饰符:
.stop | 阻止冒泡 | @click.stop=“handle” |
.prevent | 阻止默认事件 | @click.prevent=“handle” |
.capture | 捕获冒泡 | @click.capture=“handle” |
.self | 只有自身能触发 | @click.self=“handle” |
.once | 只触发一次 | @click.once=“handle” |
.passive | 不用检查,执行默认方法 | v-on:scroll.passive=“handle” |
按键修饰符 | v-on:keyup.enter=“handle” | |
系统修饰符 |
js 里的:e.stopPropagation() e.preventDefault()
addEventListener(‘click’,fn,true)
22.谈谈 Vuex 状态机:
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
getters: {
},
modules: {
},
});
State 提供唯一的公共数据源, 所有共享的数据都要统一放到 Store 的 State 中进行存储。
{
state:{
num:1};
}
---------------
this.$store.state.num;
---------------
import {
mapState} from 'vuex';
computeds:{
...mapState([num])
}
Mutation 用于变更 Store 中的数据。只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
state:{
num:1
},
mutations:{
addNum(state,n){
state.num + n;
}
}
-----------
{
{
this.$store.state.num}}
// 传入参数,第一个一定是state,第二个为传入的参数
this.$store.commit('addNum',6)
-----------
import {
mapMutations} from 'vuex';
methods:{
...mapMutations(['addNum'])
}
Action 可以包含任意异步操作,所以它用来处理异步任务。Action 提交的是 mutation,而不是直接变更状态。记住它并不能直接修改 state 里的数据,只有 mutation 能修改。
state:{
num:1
},
mutations:{
addNum(state,n){
state.num + n;
}
},
actions:{
ajaxAddNum(context,n){
setTimeout(()=>{
context.commit('add',n)
},1000)
}
}
-----------
this.$store.dispatch('ajaxAddNum','6');
-----------
import {
mapActions} from 'vuex';
methods:{
...mapActions(['ajaxAddNum'])
}
Getter 用于对 Store 中的数据进行加工处理形成新的数据。且要注意的是它并不会修改 state 中的数据。Getter 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。
state:{
num:1
},
getters:{
addNum(state){
return state.num + 1;
}
}
-------------
{
{
this.$store.getters.addNum}}
-------------
import {
mapGetters} from 'vuex';
computed:{
...mapGetters(['addNum'])
}
module 可以根据项目组件的划分来拆分 store,每个模块里管理着当前组件的状态以及行为,最后将这些模块在根 store 进行组合。
const moduleA = {
state:{
...},
mutations:{
....}
}
const moduleB = {
state:{
...},
mutations:{
....}
}
const store = new Vuex.Store({
modules:{
a:modulesA,
b:modulesB
}
})
-------------
在 Vuex 模块化中,state 是唯一会根据组合时模块的别名来添加层级的,后面的 getters、mutations 以及 actions 都是直接合并在 store 下。
console.log(store.state.a.num); // A
console.log(store.state.b.num); // B
然后getters、mutations 以及 actions跟原来一样,要保证名字不一样,因为他们直接合并在Vuex一起了
23.vue2 与 vue3 区别:
1.vue2 和 vue3 双向数据绑定原理发生了改变:
vue2 利用 es5 的 Object.defineProperty,而 vue3 利用 es6 的 Proxy 代理。
24.keep-alive 缓存组件:
vue 提供的一个缓存组件。能够缓存不活动的组件的状态。能避免多次重复渲染降低性能。
//可以在引入的子组件包裹,也可以包裹整个router-view,表示全部缓存,就是给要缓存的组件套上就行
<template>
<div>
<keep-alive>
<router-view> </router-view>
</keep-alive>
</div>
</template>
- include 属性,只有名字为设置的名字的组件才缓存:
<template>
<div>
<keep-alive include="music">
<router-view> </router-view>
</keep-alive>
</div>
</template>
- exclude,被匹配到的都不存,除了它不存,其它都存:
<template>
<div>
<keep-alive exclude="music">
<router-view> </router-view>
</keep-alive>
</div>
</template>
3.max 最多缓存几个组件:
<template>
<div>
<keep-alive exclude="music" max="2">
<router-view> </router-view>
</keep-alive>
</div>
</template>
在路由配置里定义是否缓存:
... { path:'./index', name:'index', meta:{keepAlive:true} }, ...
<template>
<div>
<keep-alive> < v-if="$route.meta.KeepAlive" /router-view> </keep-alive>
< v-if="!$route.meta.KeepAlive" /router-view>
</div>
</template>
25.vue 自定义指令:
常见指令:v-bind,v-for,v-for,v-if,v-else,v-show,v-model,v-html
全局:
<div v-color="'red'"><div>
Vue.directive('color',{
bind(el,binding){
el.style.cssText = `background-color:${
binding,value}`;
},
inserted(el,binding){
}
update(){
},
componentUpdated(){
},
unbind()
}
})
//只有bind或update简写:
Vue.directive('color',(el,binding)=>{
....})
局部:
new Vue({
el:'#app',
directives:{
color:{
bind(el,binding){
...
}}}})
26.filter 过滤器:
{
{
time | conversion('dd-mm')| again }}
-----------
filters:{
conversion(value,format){
....
return ...
}
}
-----------
Vue.filter('conversion',function(value,format){
...})
27.路由模式:
vue-router
默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。
如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState
API 来完成 URL 跳转而无须重新加载页面。
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id
就会返回 404,这就不好看了。
五. 前端基础延伸的会
promise 封装 ajax 请求:
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = "json";
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
};
});
}
sendAJAX("http/123...").then(
(value) => {
console.log(value);
},
(reason) => {
console.warn(reason);
}
);
promise 链式调用 :
如果中途发生reject,便会终止执行。
(1) promise 的 then()返回一个新的 promise,可以开成 then()的链式调用。
(2)通过 then 的链式调用串连多个同步/异步任务。
pendding resolved/fullfilled rejected
*如果抛出异常, 新 promise 变为 rejected, reaon 为抛出的异常。
*如果返回的是非 prormise 的任意值,新 promise 变为 resolved, value 为返回的值。 *如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果。
let p = new Promise((resolve, reject) => {
resolve("yes");
});
p.then((value) => {
return new Promise((resolve, reject) => {
// 返回一个promise成功状态结果
resolve("oh yes ~~~");
});
//补充,如果我想在这中断promise链,可以return返回的是一个pendding结果
// pendding如: return new Promise(()=>{})
})
.then((value) => {
console.log(value); // 输出oh yes ~~~,收到了上次的成功结果
})
.then((value) => {
console.log(value); // 输出undefine,应该是上次没有返回结果,但是应该也算成功的
})
.then((value) => {
throw "oh NO"; //抛个异常
})
.catch((reason) => {
//异常穿透,(1)当使用promise的then链式调用时,可以在最后指定失败的回调。(2)前面任何操作出 了异常,都会传到最后失败的回调中处理。
console.warn(reason); // 输出oh NO
});
1.Promise.all
包含 n 个 promise 数组。返回一个新的 promise。只有所有 promise 都成功才成功,只有有一个失败就失败,返回最先失败那个值。它在处理多个异步请求时很好。这个新的 promise 对象在触发成功状态以后,会把⼀个包含 iterable ⾥所 有 promise 返回值的数组作为成功回调的返回值
如:
let p1 = new Promise((resolve, reject) => {
resolve("yes");
});
let p2 = new Promise((resolve, reject) => {
reject("no!");
});
//失败结果:
Promise.all([p1, p2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
2.Promise.race:
race 为赛跑的意思,哪个结果获取得快就返回哪个结果,不论成功或者失败。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1 win");
}, 10);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2 win");
}, 9);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3 win");
}, 9);
});
Promise.race([p1, p2, p3]).then(
(value) => {
console.log(value); // p2 win
},
(reason) => {
console.warn(reason);
}
);
Promise 扩展:
promise 的实例方法:
.then() ; .catch() ; .finally() ;
promise 的静态方法:
Promise.all() ; Promise.allSettled() ; Promise.race();
Promise.resolve(value) ; Promise.reject(reason)
Promise.prototype.finally()
该⽅法⽤于指定不管 Promise 对象最后状态如何,都会执行它。
new Promise((resolve, reject) => {
resolve("666");
})
.then((value) => {
console.log(value);
})
.finally(() => {
console.log("最后总会执行我");
});
Promise.allSettled(iterable)
等到所有 promises 都已敲定(settled)(每个 promise 都已兑现(fulfilled)或已拒绝 (rejected))。返回⼀个 promise,该 promise 在所有 promise 完成后完成。并带有⼀个对象数组,每个对象对应每个 promise 的结果。
https://blog.csdn.net/luo1831251387/article/details/115643059
3.history 与 hash 的区别:
hash:地址栏出现 # 字符,如http://www.baidu.com/#/hello,hash 的值为 #/hello。特点:hash 不会包括在 http 请求中,对后端无影响,改变 hash 值不会重新发送请求。
history:地址没有 # 字符。特点:当改变 url 地址回车刷新页面时,如果地址与实际后端发起请求 URL 不一致会返回 404 错误。
4.前端性能如何优化:
1.减少 http 请求,如使用精灵图。2、css 放在文件头部,js 文件放在底部。3、图片懒加载。4、减少回流与重绘。5.防抖与节流。
5.说一说 JSON:
一种数据交换格式语言,取代繁重的 XML。
6. async 与 await :
优点:
1.它做到了真正的串行的同步写法,代码阅读相对容易。代码变同步
2.于条件语句和其他流程语句比较友好,可以直接写到判断条件里面。
3.处理复杂流程时,在代码清晰度方面有优势。
缺点:
1.无法处理 promise 返回的 reject 对象,要借助 try…catch…
2.await 只能串行,做不到并行。可能会阻塞代码执行。导致性能问题。
3.async/await 无法简单实现 Promise 的各种原生方法,比如.race()之类。
4.try…catch…内部的变量无法传递给下一个 try…catch…
async function shi(){
let res = axios({
url:'https://api.muxiaoguo.cn/api/Gushici',
method:'get'
})
console.log(res.data.data.Poetry)
}
链式调用:
async function getDate(){
try{
let result1 = await ajaxGet('data1.json'); // 如果执行到这里报错,直接跳至下面 catch() 语句
let result2 = await ajaxGet(result1.url);
let result3 = await ajaxGet(result2.url);
}catch(err){
console.log(err)
}
}
7. 写一个基本的 ajax 请求:
AJAX 是一种无需重新加载整个网页的情况下,能够更新部分网页的技术。
AJAX 是开发者的梦想,因为您能够:
- 不刷新页面更新网页
- 在页面加载后从服务器请求数据
- 在页面加载后从服务器接收数据
- 在后台向服务器发送数据
// 1.创建XMLHttpRuquest对象
const 名字 = new XMLHttpRequest();
//如让名字=xhr;
// 2. 初始化,设置交互信息 设置请求的方法与 url请求地址
xhr.open('GET','http://............?'+'name:'+'zhangsan'+'&'+'mima:'+'123456');
// 如果默认可不设置requestHeader
// xhr.setRequestHeader('Content-type','application/json;charset=utf-8');
// 3. 请求发送
xhr.send();
// 4.事件绑定,处理服务端返回结果
xhr.onreadystatechange = function(){
//判断服务端返回了所有结果
if(xhr.readyState === 4){
//判断响应状态码 200 404 403 500 ....等
// 2xx 都表示成功
if(xhr.status>=200&&xhr.status<300){
// 此处处理结果;
// console.log(xhr.status); 可得状态码
// console.log(xhr.statusText); 可得状态字符串
// console.log(xhr.getAllResponseHeaders()); 可得所有响应头
// console.log(xhr.response); 可得响应体
....此处对得到进行数据进行处理....略......
}else{
}
}
}
//-----------------------------------------------
get请求传参数也可以下载QS序列化工具插件
var login = {
name:'zhang',mima:'123'};
var xhr = new XMLHttpRequest();
xhr.open('get','http//...?'+qs.stringify(login));
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState==4&&xhr.status==200){
}
}
//post请求传参数
var login = {
name:'zhang',mima:'123'};
var xhr = new XMLHttpRequest();
xhr.open('post','http//...');
xhr.setRequestHeader('Content-type','application/json;charset=utf-8');
xhr.send(JSON.stringify(login));
xhr.onreadystatechange = function(){
if(xhr.readyState==4&&xhr.status==200){
}
}
// JQury形式的ajax请求:
-
优点:
-
- 通过异步模式,提升了用户体验.
- 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用.
- Ajax 在客户端运行,承担了一部分本来由服务器承担的工作,减少了大用户量下的服务器负载。
- Ajax 可以实现动态不刷新(局部刷新)
-
缺点:
-
- 安全问题 AJAX 暴露了与服务器交互的细节。
- 对搜索引擎的支持比较弱。
不容易调试。
其中 readState 的 5 个状态:
0: 还没调用open方法
1: 未调用send方法,也就是还没发送数据给服务器
2: 还没有接收到响应
3: 开始接收到了部分数据
4: 接收完成,数据可以在客户端使用了
8.vue 性能优化:
- 对象层级不要过深,否则性能就会差
- 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
- 大数据列表和表格性能优化-虚拟列表/虚拟表格
- 防止内部泄漏,组件销毁后把全局变量和事件销毁
- 图片懒加载
- 路由懒加载
- 第三方插件的按需引入
- 适当采用 keep-alive 缓存组件
- 防抖、节流运用
- 服务端渲染 SSR or 预渲染
9.原型重构数组方法:
filter: Array.prototype.myFilter = function (fn, obj) {
var arr = [];
for (var i = 0; i < this.length; i++) {
if (obj ? fn.bind(obj)(this[i], i, this) : fn(this[i], i, this)) {
arr.push(this[i]);
}
}
return arr;
};
unshift: Array.prototype.myUnShift = function (...t) {
var arr = [...t, ...this];
for (var i = 0; i < arr.length; i++) {
this[i] = arr[i];
}
return arr.length;
};
forEach:
Array.prototype.myForEach = function(fn){
for(var i=0;i<this.length;i++){
return fn(this[i],i,this);
}
}
map:
Array.prototype.myMap = function(fn,obj){
var arr = [];
for(var i=0;i<this.length;i++){
arr[i]= obj?obj.call(obj,this[i],i,this):fn(this[i],i,this);
}
return arr
}
10.Git 常用命令:
-
初始化仓库 git init
-
克隆一个仓库 git clone xxx.git
-
查看远程仓库地址 git remote -v
-
绑定远程仓库地址 git remote add origin xxx.git
-
删除某个已绑定的地址(xxx 是源名称) git remote remove xxx
-
更新 git pull origin master
-
提交文件 git add -A (-A 代表全体文件,也可以是具体文件名)
-
提交信息(放到本地仓库)git commit -m ‘xxx’
-
提交代码到远程仓库 git push origin master
-
查看提交历史: git log --stat
-
工作区撤回: git checkout
-
提交后撤回 git rest HEAD^1 默认是 1
-
当前节点新建分支 git checkout -b 《name》
-
列举所以分支 git branch
-
切换到某个分支 git checkout 《name》
-
删掉特定分支 git branch —D 《name》
-
合并分支(需要回到主分支) git merge 《name》
11.Linus 常用命令:
添加用户: adduser 用户名
添加组: addgrounp 组名
查看用户信息: cat /etc/passwd 应该是 cat 查看 vi编辑
查看组信息: cat/etc/group
用户更改自己密码: passwd
root改用户密码: passwd 用户
清屏:clear
列出历史命令: history
切换到root用户: su
切换到普通用户(环境不变): su 用户
切换到普通用户(切换到用户自己的家目录): su-用户
查看当前路径: pwd
回到跟目录: cd /
回到上一级目录: cd ..
回到家目录: cd ~
去到下一级指定目录: cd 目录名
查看当前目录下的目录或文件: ls
长格式查看当前目录下的目录或文件: ls -l
查看文件内容: cat/etc/passwd
创建文件:toush 文件名
创建目录:mkdir 目录名
创建层次目录(若a不存在也创建): mkdir -p a/b
移动目录或文件(a目录移动到b目录):mv /a //b
把源文件不改名复制到 b 目录下:cp hello.txt b
改名复制把源文件复制到 b 目录下: cp hello.txt b/hello2.txt
删除目录: rmdir 目录名
强制删除有子层次的目录:rm -rf 目录名
删除文件:rm 文件名
递归删除非空目录中的内容:rm -R 文件名
查找文件: find 文件名
chmod 777 文件名 给文件rwx权限
进入编辑模式:vi/vim 文件名
进入输入字符模式:i
退出输入字符,开启命令模式:esc
开启底层命令模式: :
底层命令: 退出:q 保存: w 保存成新文件: w newfile
保存离开: wq 强制退出不保存:q!
Linux远程登录命令: ssh [email protected]
远程文件传输命令: scp hello.txt [email protected]
查看Java进程命令: ps -ef|grep java
运行jar包步骤:
1.新建一个数据库,数据库名,用户名,密码名,后端给。
2.运行SQL文件,同时给用户这个数据库的权限。然后用filezilla把jar包上传。
3.登录云服务器 ssh [email protected] ,去到jar包在的目录。
4.运行jar包:nohup Java -jar xxx.jar &
5.查看Java进程: ps -ef|grep java
6.删除某个jar包进程:kill -9 pid
12.正则表达式:
13.axios 常用方法: get、post、put、patch、delete
Get:
1.-------
axios({
url:"http//...",
method:"get",
params:{
id: 123
}
}).then(response=>{
console.log(response)
})
2.---------
axios.get('http//...?id=123').then(response=>{
console.log(response)
})
3.--------
axios.get('http//...',{
params:{
id:123}
}).then(response=>{
console.log(response)
})
4.-----------
async function getHttp(){
try{
let response = await axios.get('http//...?id=123');
console.log(response);
}catch(error){
console.error(error);
}
}
Post:
1.----------
axios({
url:"http//...",
method:"post",
data:{
page:10
}
}).then(response=>{
console.log(response)
})
2.---------
axios.post('http//...',{
page:10}).then(response=>{
console.log(response)
})
axios 默认配置:
axios.defaults.method="get";
axios.defaults.bassURL = "http//..."
.....
创建 axios 实例:
const myAxios = axios.creat({
baseURL = "http//..."
timeout:2000 // 2000指请求超时则中断。
})
//
myAxios.get('/data').then(res=>{})
14.解构赋值:
- 数组解构。完全解构: let [a,b,c,d,e] = arr 。不想解构的值跳过: let [a,d,e] = arr 。赋予默认值: let [a,b,c = 3] = [1,2] ,如果数组长度足够,默认值无效。剩余项解构: let [a,…b] = arr 。
- 对象解构。同名解构:let {name,age} = obj 。更名解构:let {name:xinming,age:nianling} = obj 。 未完。。。。。。。。。。。。。
- 字符串解构。var str=“hello”;let [a,b,c]=str;/ /a->h //b->e //c->l 。
- let {push} = [] ; 解构出数组的 push 方法,为一个函数。
15.常见的异步编程:
- 回调函数
- 事件监听
- Promise
- 定时器
- async 函数
- generator(带*的函数)函数
16.防抖与节流:
防抖:
function fn(real, delay) {
var time = null;
return function () {
if (time) {
clearTimeout(time);
}
time = setTimeout(() => {
real(...arguments);
}, delay);
};
}
防抖(第一次不延迟):
function fn(real, delay) {
var time = null;
return function () {
var p = !time;
if (p) {
real(...arguments);
}
if (time) {
clearTimeout(time);
}
time = setTimeout(() => {
time = null;
}, delay);
};
}
节流:
function fn(real, delay) {
var lastTime = 0;
return function () {
var nowTime = new Date().getTime();
if (nowTime - lastTime > delay) {
real(...arguments);
lastTime = nowTime;
}
};
}
节流(定时器实现):
function fn(real, delay) {
var flag = true;
return function () {
if (flag) {
setTimeout(() => {
real(...arguments);
flag = true;
}, delay);
}
flag = false;
};
}
17. js 函数柯里化:
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
//实现add(1)=1,add(1,2)=3,add(1)(3) = 4...
function curryAdd() {
var args = [...arguments];
function temp() {
args.push(...arguments);
}
temp.toString = function () {
return args.reduce(function (a, b) {
return a + b;
});
};
return temp;
}
18.图片懒加载:
js:
<img data-src="img/1.jpg" src="img/0.png" alt="xxx" />
<img data-src="img/2.jpg" src="img/0.png" alt="xxx" />
<img data-src="img/3.jpg" src="img/0.png" alt="xxx" />
<script>
// 1.---------------
var images = document.getElementsByTagName("img");
window.addEventListener('scorll',e=>{
ergodic();
})
function ergodic(){
for (let i of images) {
if(i.offsetTop<=windwo.innerHeight+window.scrollY){
let url = i.getAttribute('data-src');
i.setAttribute('src',url);
}
}
}
// 2-------------------------------------------
</script>
vue 自定义指令实现:
Vue.directive('lazy',{
inserted(el,binding){
let observer = new IntersectionObserver((entries)=>{
for(let i in entries){
if(i.isIntersecting>0){
i.target.src = binding.value;
observer.unobserve(i.target);
}
}
})
observer.observe(el);
}
})
<img v-lazy="'img/1.jpg'"></img>
19. JQuery 一些用法:
20. axios 拦截器:
应用场景:
1:每个请求都带上的参数,比如token,时间戳等。
2:对返回的状态进行判断,比如token是否过期
//请求拦截器
axios.interceptors.request.use(
function (config) {
// 在发送请求之前做些什么
console.log("请求成功喽!");
return config;
},
function (error) {
// 对请求错误做些什么
console.log("请求失败了~");
return Promise.reject(error);
}
);
//响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据做点什么
console.log("响应成功啦!");
return response;
},
function (error) {
// 对响应错误做点什么
console.log("响应失败阿~");
return Promise.reject(error);
}
);
应用,携带 token 与验证状态码:
//发送请求前携带token
axios.interceptors.request.use(config=>{
const token = window.localStorage.getItem('token');
config.headers.Authorization = token;
//好了,继续发送请求吧
return config;
},error=>{
return Promise.reject(error)
})
// 对响应状态码进行判断
axios.interceptors.response.use(response=>{
if(response.status === 200 ){
//没毛病,继续返回正确结果把
return Promise.resolve(response);
}else{
return Promise.reject(response);
}
},error=>{
// 服务器状态码不是2开头的的情况
// 这里可以跟你们的后台开发人员协商好统一的错误状态码
// 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
if(error.response.status){
switch(error.response.status){
case 401: ....;
case 403: ....;
case 404: ....;
...
default: ....;
}
}
return Promise.reject(error.response)
})
21.axios 请求模块化封装:
六. 扩展:
1. 类数组对象如何使用数组方法:
说到底只是我定义的类数组对象原型上没有数组原型上的方法。只要让其能使用这些方法就行。
//如先从数组里解构获得 push ,forEach 和 unshift 方法 ,原理就是既然是对象解构,那就从数组的原型上找到这个属性并赋值给变量
//Array.prototype.push = function(){}
let {
push} = [];
let {
unshift} = [];
let {
forEach} = [];
console.log(push);
// 定义一个类数组对象
var lsz = {
'0':'1','1':'1',length:2};
// 使用
push.call(lsz,'3');
unshift.call(lsz,'-1');
forEach.call(lsz,function(item,index){
console.log(item,index);})
console.log(lsz);
--------------------
var lsz = {
'0':'1','1':'1',length:2};
Object.setPrototypeOf(lsz,Array.prototype);
lsz.push('30')
console.log(lsz);
-------------------------
var lsz = {
'0':'1','1':'1',length:2};
lsz.__proto__ = Array.prototype
lsz.push('30')
console.log(lsz);
------------------------
var lsz = {
'0':'1','1':'1',length:2};
Array.prototype.push.call(lsz,'a');
console.log(lsz);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1lcD3qMa-1657174227835)(C:\Users\夜\AppData\Roaming\Typora\typora-user-images\image-20210916155912011.png)]
2.使类数组对象可以用 for of 迭代:
有 Symbol.iterator 这个属性才能被迭代。
可以自定义它的 Symbol.iterator 属性方法:
let obj = {
0: "000", 1: "111", length: 2 };
obj[Symbol.iterator] = function* () {
Object.defineProperty(this, "length", {
enumerable: false,
value: this.length,
});
for (let key in this) {
yield [key, this[key]];
}
};
// 可以被迭代了
for (let i of obj) {
console.log(i);
}
也可以借数组的 Symbol.iterator 用用,它遍历的 i 是 value:
let obj = {
0: "000", 1: "111", length: 2 };
obj[Symbol.iterator] = [][Symbol.iterator];
for (let i of obj) {
console.log(i);
}
3. iterator 迭代器和 Generator 生成器
形式上来看,Generator 函数是⼀个普通函数,但是有两个特征。⼀是, function 关键 字与函数名之间有⼀个星号;⼆是,函数体内部使⽤ yield 表达式,定义不同的内部状态。调⽤ Generator 函数,返回⼀个遍历器对象,代表 Generator 函数的内部指针。以后,每 次调⽤遍历器对象的 next ⽅法,就会返回⼀个有着 value 和 done 两个属性的对象。next ⽅法可以带⼀个参数,该参数就会被当作上⼀个 yield 表达式的返回值。
function* gen() {
let first = yield "hello";
console.log(first);
yield "world";
}
let iterator = gen();
iterator.next(); //
iterator.next(6); // 6
当需要对⼀个对象进⾏迭代时(⽐如开始⽤于⼀个 for…of 循环中),它的 Symbol.i terator ⽅法都会在不传参情况下被调⽤,返回的迭代器⽤于获取要迭代的值。Iterator 的遍历过程如下:
- 创建⼀个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是⼀ 个指针对象。 2. 第⼀次调⽤指针对象的 next ⽅法,可以将指针指向数据结构的第⼀个成员。 3. 第⼆次调⽤指针对象的 next ⽅法,指针就指向数据结构的第⼆个成员。 4. 不断调⽤指针对象的 next ⽅法,直到它指向数据结构的结束位置。 每⼀次调⽤ next ⽅法,都会返回数据结构的当前成员的信息。具体来说,就是返回⼀个 包含 value 和 done 两个属性的对象。其中, value 属性是当前成员的值, done 属性是 ⼀个布尔值,表示遍历是否结束。
4.Set 与 Map:
Set 允许存储任何类型的唯⼀值,Set 是 ES6 提供了新的数据结构。它类似于数组,但是成 员的值都是唯⼀的,没有重复的值。
const set = new Set ([1,22,2,2,2,3,4])
//数组去重
[...new Set([1,1,2,3])]
Set.prototype.size
获取容器中元素个数
Set.prototype.add(value)
向集合加⼊⼀个元素,不允许相同元素存在
Set.prototype.delete(value)
从集合中删除⼀个元素
Set.prototype.has(value)
判断集合中是否包含 value
Set.prototype.clear()
清空 set 集合
Set.prototype.keys()
获取 set 集合 key 值的迭代器,由于 set 集合没有 key 所有与 values ⽅法返回结果⼀致
Set.prototype.values()
获取 set 集合的 value 值的迭代器
Set.prototype.entries()
获取 set 集合的 entries 迭代器
Set.prototype.forEach()
与数组类似的迭代⽅法
Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种 类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应, Map 结构提供了“值—值”的对应,是⼀种更完善的 Hash 结构实现。如果你需要“键值对”的数 据结构,Map ⽐ Object 更合适。同时,键名也是唯一的。
const map = new Map();
map.set("key", "value");
console.log(map);
Map.prototype.size
获取集合键值对个数
Map.prototype.set(key, value)
向集合中设置⼀个键值对
Map.prototype.get(key)
从集合中通过 key 获取 value
Map.prototype.has(key)
判断集合中是否包含 key 指定的键
Map.prototype.delete(key)
通过 key 删除⼀个键值对
Map.prototype.clear()
清空 map 集合
Map.prototype.keys()
获取 map 集合 key 值的迭代器
Map.prototype.values()
获取 map 集合 value 值的迭代器
Map.prototype.entries()
获取 map 集合 entry 的迭代器
Map.prototype.forEach
5.typescript 基本语法:
首先,强类型不允许随意的隐式类型转换,而弱类型是允许的。JavaScript 就是经典的弱类型语言。而 Typescript 可以说是 JavaScript 的超集,在 js 的基础上新增了许多语法特性,使得类型不再可以随意转换,能大大减少开发阶段的错误。
- 声明基本类型:
const a: number = 1;
const b: string = 2;
const c: boolean = true;
const d: null = null;
const e: undefine = undefine;
const f: symbol = Symbol();
-
引用类型:
const obj: { name:number} = { name:213} ----------------- const arr<number> = [1,2,3] const arr:number[] = [1,2,3] ------------------- const tuple:[number,string] = [123,'asd'] ------------------ enum Status { a=4, b=5, c } ------------------- function fun(arg:number):number{ return arg } function fun(arg:number,arg2?:number):number{ return arg }
3.类型断言:
const res: number = 1; const a = res as number;
4.基本接口:
接口可以理解为一种规范,一种契约。可以约束一个对象里应该有哪些成员,这些成员都是怎么样的。
interface Post { name:string; age?:number; readonly dui:boolean } let post: Post { name:'222'; age:12; dui:true; } -------------- interface Post { [prop:number]:number }
5.类:
描述一类具体事物的抽象特征。
class Person { // 公共的 只读属性 public readonly name: string; // 私有的,只有自己可访问 private age: number; // 受保护的,子类可访问 protected like: string; constructor(name: string, age: number) { this.name = name; this.age = age; } }
6.一些类与类之间有些许共同的特征,这些共同的特征可以抽象成为接口。
interface Eat {
eat(foo:string):void
}
class Person implements Eat {
eat(foo:string){
....
}
}
class Animal implements Eat {
eat(foo:string){
....
}
}
- 抽象类:
约束子类必须有某些成员,有点类似接口,不同的是抽象类可以包含一些具体的实现。比如动物类应该为一个抽象类,它的子类有猫,狗,熊猫等。它们都是动物,也有一些共同的特征。定义一个类为抽象类后,就不能再 new 实例了,只能被其子类继承。
其中abstract 定义抽象类,类里用abstract定义一个抽象方法,子类必须实现抽象方法。
abstract class Animal {
abstract run(arg: number): void;
}
class Dog extends Animal {
run(arg: number): void {
}
}
8.泛型:
泛型就是在定义函数,接口或者类的时候没有指定具体类型,等到使用时才指定具体类型。极大程度的复用代码。
function fun<T>(arg:T):T{
return T
}
-----------
interface Post<V,M>{
value:V
message:M
}
-----------
class Animal<T>{
zeroValue: T;
add: (x:T,y:T)=>T;
}