当前位置:首页 » 《随便一记》 » 正文

2023前端面试题汇总

19 人参与  2023年04月10日 15:51  分类 : 《随便一记》  评论

点击全文阅读


今天想把近期看的面试题汇总一下,项目空档期,看的面试题比较多,但没有整理,导致回忆的时候,想不起来的还要重新查找。包括身边朋友面试遇到的题目也总结了进去。总结的都是前端基础(初级)面试题。三月份我会持续更新~~~ 浅卷一下下2023.03.09开始——>3.15——>3.20——3.23

一、CSS面试题

1. 三种CSS隐藏元素的方式有哪些?

设置display: none;样式。这个样式会让元素在页面上彻底消失。元素本来占有的空间,也会被其他元素占有,所以,他会导致浏览器的重排和重绘。第二种方式设置visibilty:hidden;的样式。它和display: none;的区别在于,元素在页面消失之后,它原本占有的空间依然会保留,所以,他只会导致浏览器的重绘,而不会重排。第三种是设置opcity:0;透明度为0。在视觉上元素也是隐藏的,所以这也是一种隐藏元素的方式。还有其他方法,利用定位,让元素不出现在可视区域,等等。

2. CSS 盒模型?

盒模型是什么?盒模型的分类?怎么指定盒模型?

1、每个html元素都可以看作一个盒子,这个盒子由里到外,由这个元素的内容content、边框border、内边距padding、外边距margin组成。

2、盒子模型一般分为标准盒模型 和 怪异盒模型,怪异盒模型又叫做IE盒模型。这两种盒模型又有什么区别呢?

3、在标准盒模型下,浏览器的width属性,就是内容content的宽度,也就是说,如果我们给一个元素设置width属性,那么width属性就是内容的宽度,此时这个元素盒子的总宽度就是:width + 内边距 + 边框 + 外边距,高度也是这样。而怪异盒模型,指的是浏览器的width属性不是内容的宽度,是元素的内容 + 内边距 + 边框的宽度之和。
换句话说,如果我们给一个元素设置width属性,那么这个盒子的总宽度就是:width + 外边距 之和。因为width已经包含了内容、内边距、边框。
正常情况下默认是标准盒模型,但是我们可以通过box-sizing属性来指定盒模型,当它的值是border-box时,就是怪异盒模型。当值content-box时,就是标准盒模型,因为标准盒模型的width就是content。

3. 页面布局有哪几种方式?

页面布局常用的方法有浮动、定位、flex、grid网格布局、栅格系统布局 浮动:优点: 兼容性好。缺点:浮动会脱离标准文档流,因此要清除浮动。我们解决好这个问题即可。绝对定位。优点: 快捷。缺点: 导致子元素也脱离了标准文档流,可实用性差。flex 布局 (CSS3中出现的)。优点: 解决上面两个方法的不足,fex布局比较完美。移动端基本用 flex布局。网格布局 (grid)。CSS3中引入的布局,很好用。代码量简化了很多。 利用网格布局实现的个左右300px中间自适应的布局:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Document</title><style>html * {padding: 0;margin: 0;}/* 重要:设置容器为网格布局,宽度为100% */.layout.grid .left-center-right {display: grid;width: 100%;grid-template-rows: 100px;grid-template-columns: 300px auto 300px; /* 重要:设置网格为三列,并设置每列的宽度。即可。*/}.layout.grid .left {background: red;}.layout.grid .center {background: green;}.layout.grid .right {background: blue;}</style></head><body><section class="layout grid"><article class="left-center-right"><div class="left">我是 left</div><div class="center">我是 center<h1>网格布局解决方案</h1></div><div class="right">我是 right</div></article></section></body></html>

4. rem 和 em?

rem 和 em都是css中的相对长度单位,他们的区别就是,rem是相对于根元素字体大小进行计算的,任意浏览器的默认字体都是16px,所以一般 1rem = 16px。而em是相对于当前元素的字体大小来计算的,但是为什么我们经常说相对于父元素呢,其实也很好理解,因为font-size字体大小这个属性是可以被继承的,所以父元素的字体大小势必会影响到他的子元素的字体大小,也就是说子元素如果没有自己的font-size,那么它的font-size就会继承父元素,那此时 1em 的值就可以认为是相对于父元素字体大小来计算。另外rem是css3新增的一个相对单位,r就是root根的缩写,它的出现也就是为了解决em的缺点,em是相对于父元素或当前元素的字体大小进行换算的,当当前元素或父元素字体大小改变时,就又得重新计算了,那么层级较多的时候,换算也就会越来越复杂,而rem只相对于HTML根元素,有了rem这个单位我们只需要调整根元素HTML的font-size,就能达到所有元素的动态适配,避免了一些复杂的层级关系。

补充:

rem是相对于根元素进行计算,而em是相对于当前元素或父元素的字体大小。
rem不仅可以设置字体的大小,还支持元素宽、高等属性。em是相对于当前元素或父元素进行换算,层级越深,换算越复杂。而rem是相对于根元素计算,避免层级关系。

5. 简单谈一下flex布局?

当并列书写多个div标签,它们会纵向向下排位,如果我们想将多个div并列成一排,就得借助position,float,或display属性,这便是传统的盒模型做法。而flex布局则是一种新的布局方案,通过为修改父div的display属性,让父元素成为一个flex容器,从而可以自由的操作容器中子元素(项目)的排列方式。例如我们让多个div横向排列,传统做法是使用浮动,但浮空后因为脱离文档流的缘故,父元素会失去高度,这又涉及了清除浮动等一系列的问题。而flex布局相对简单很多,修改父元素display:flex,你会发现div自动就排列成了一行,而且没有浮动之后的副作用,从回流角度考虑,flex的性能更优于float;随着浏览器不断兼容以及旧版本的淘汰,flex布局注定会成为更为流行的布局方案。

1、flex-direction 属性,决定主轴的方向(即项目的排列方向)
取值:row(默认) | row-reverse | column | column-reverse
2、flex-wrap属性,决定容器内项目是否可换行。
取值:nowrap(默认) | wrap | wrap-reverse
3、justify-content属性,定义了项目在主轴的对齐方式。
取值:flex-start(默认) | flex-end | center | space-between | space-around | space-evenly;
4、align-items属性,定义了项目在交叉轴上的对齐方式。
取值:flex-start | flex-end | center | baseline | stretch(默认)
5、flex-flow: flex-direction 和 f’lex-wrap 的简写形式。
取值:flex-flow: || ;默认值为row nowrap,没什么卵用。
6、align-content: 定义了多根轴线的对齐方式,如果项目只有一根轴线,那么该属性将不起作用。
取值: align-content: flex-start | flex-end | center | space-between | space-around | stretch;

有六种属性可运用在 item 项目上:

orderflex-basis: auto;定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间flex-grow: 0; flex-grow定义项目的放大比例flex-shrink: 1; 定义了项目的缩小比例flexalign-self

6. 什么是重排(回流)和重绘?

重绘:修改了页面元素的字体颜色、背景颜色、边框颜色,但是并不影响页面布局,因为元素的尺寸大小并没有改变,其他元素也并没有受到位置上的影响,还有设置opcity透明度,visibity:hidden;属性也会导致重绘。总结:当页面元素样式改变而不影响布局时,浏览器重新对元素进行更新的过程叫做重绘。重排:是指修改了元素的尺寸大小,例如改变了元素的宽高、边框的粗细,或者是给某元素设置display:none;属性,这样也会导致回流。总结:当页面元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程叫做重排也叫做回流。所以回流一定会导致重绘。

不管页面发生了重绘还是重排,它们都会影响性能(最可怕的是重排 ,应尽量避免)

7. 哪些情况会发生重排?

页面初始渲染添加/删除可见DOM元素改变元素位置改变元素尺寸(宽、高、内外边距、边框等)改变元素内容(文本或图片等)改变窗口尺寸

8. 如何减少重排和重绘(提高性能)?

由于回流和重绘会带来很大的性能开销,所以在开发中我们要尽量避免或减少回流和重绘的次数来提高性能

避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。对具有复杂动画的元素使用绝对定位,使其脱离文档流,否则会引起父元素及后续元素频繁回流。要避免频繁的去操作DOM,可以通过创建documentFragment,完成所有所有DOM操作后,最后再把它添加到文档中。避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。

9. 页面渲染的过程?(浏览器是怎么渲染页面的?)

解析HTML代码结构生成 DOM 树。就是将代码解析为一颗DOM Tree,按照从上到下,从左到右的顺序,将树上的节点压入文档流然后布局。解析CSS文件,生成CSSOM(css Object Model)。顺序为:浏览器默认样式->自定义样式->页面内的样式。将DOM和CSSOM结合起来就形成了Render树。这个渲染树和DOM树的不同之处在于,它是受样式影响的。它不包括那些不可见的节点。当渲染树生成之后,浏览器就会根据渲染树在屏幕上渲染和展示。

其中解析HTML的时候会将HTML文档转换为DOM树,构建render树的时候会将DOM树转换为更加结构化的渲染树,布局和绘制render树的时候会将渲染树转换为可见的像素画面。

10. 如何水平垂直居中?

1. 通过定位,给父盒子相对定位,子盒子绝对定位,top、left为50%,再margin-left : -(子盒子宽的一半)px; margin-top: -(子盒子高的一半)px;<style>        div {            position: relative;            height: 400px;            width: 400px;            background-color: pink;        }        span {            position: absolute;            top: 50%;            left: 50%;            margin-left: -50px;            margin-top: -50px;            display: block;            width: 100px;            height: 100px;            background-color: purple;        }</style>2. 不依赖通过计算子盒子的宽高进行定位,可以用transform: translate 移动自身的一半就行了。<style>        div {            position: relative;            height: 400px;            width: 400px;            background-color: pink;        }        span {            position: absolute;            top: 50%;            left: 50%;            transform: translate(-50%, -50%);            display: block;            width: 100px;            height: 100px;            background-color: purple;        }</style>3. 通过flex布局,设置垂直水平都居中。<style>        div {            display: flex;            justify-content: center;            align-items: center;            height: 400px;            width: 400px;            background-color: pink;        }        span {            display: block;            width: 100px;            height: 100px;            background-color: purple;        }</style>

11. 移动端适配

之前做前端适配,要么通过使用css的media媒体查询,要么通过获取可视宽度按照想要的比例设置文档根字体大小。无意间在网上看到一种新的适配插件,postcss-px-to-viewport,就是用viewport(vw)单位,viewport单位越来越受到众多浏览器的支持,postcss-px-to-viewport,将px单位按照相应的配置自动转换成需要的viewport单位。使用插件:postcss-px-to-viewport。可使用npm、yarn安装:npm install -D postcss-px-to-viewport 或 yarn add -D postcss-px-to-viewport 。 在项目的最外层(即配置文件同级目录下)新建.postcssrc.js文件,在.postcssrc.js文件中加入配置语句,.postcssrc.js更新需要重启才会生效,以下为示例:
module.exports = {    plugins: {        autoprefixer: {}, // 自动添加相应浏览器前缀,样式兼容,如-webkit-,-moz-等        "postcss-px-to-viewport": {            unitToConvert: "px", // (String)需要转换的单位,默认为"px"            viewportWidth: 750, // (Number)设计稿的视口宽度:可为设计稿的宽度,也可按需自行配置(PC)            unitPrecision: 6, // (Number)单位转换后保留的精度,即小数点位数            propList: ["*"], // (Array)指定转换的css属性的单位,*代表全部css属性的单位都进行转换            viewportUnit: "vw", // (String)指定需要转换成的视窗单位,默认vw            fontViewportUnit: "vw", // (String)指定字体需要转换成的视窗单位,默认vw            selectorBlackList: ["demo"], // (Array)需要忽略的CSS选择器,不转为视窗单位,使用原有单位,            minPixelValue: 1, // (Number)设置最小的转换数值,默认为1,只有大于1的值会被转换            mediaQuery: true, // (Boolean)媒体查询里的单位是否需要转换单位,默认false            replace: true, // (Boolean)是否直接更换属性值,而不添加备用属性            exclude: [/node_modules/], // (Array or Regexp)忽略某些文件夹下的文件或特定文件,用正则做目录名匹配            include: /Ignore.vue/,    //(Array or Regexp)只有匹配到的文件才会被转换,那将只有匹配到的文件才会被转换            landscape: false, // (Boolean)是否添加根据 landscapeWidth 生成的媒体查询条件            landscapeUnit: 'vw',    //(String)横屏时使用的单位            landscapeWidth: 700     //(Number)横屏时使用的视口宽度        }    }};
也可在配置文件中进行配置 vue.config.js。
const pxtoviewport = require("postcss-px-to-viewport");module.exports = {  css: {    // 忽略 CSS order 顺序警告    extract: { ignoreOrder: true },    // 适配插件    loaderOptions: {      postcss: {        plugins: [          pxtoviewport({            // 配置视窗口尺寸            viewportWidth: 1700,            unitPrecision: 5,            viewportUnit: "vw",            fontViewportUnit: "vw",            selectorBlackList: [],            minPixelValue: 1,            mediaQuery: true,          }),        ],      },    },  },}
使用 lib-flexible 动态设置 REM 基准值(html 标签的字体大小) 安装: yarn add amfe-flexible 或者 npm i amfe-flexible然后在 main.js 中加载执行该模块:import ‘amfe-flexible’最后测试:在浏览器中切换不同的手机设备尺寸,观察 html 标签 font-size 的变化。 使用 postcss-pxtorem 将 px 转为 rem 安装:yarn add -D postcss-pxtorem,npm install postcss-pxtorem -D。( -D 是 --save-dev 的简写。)然后在项目根目录中创建 .postcssrc.js 文件。配置完毕,重新启动服务。
module.exports = {  plugins: {    'postcss-pxtorem': {      rootValue: 37.5,      propList: ['*']    }  }}

12. 什么是BFC?其规则是什么?怎么触发BFC?BFC能够解决什么问题?

【 什么是BFC?】BFC的全称是block-formatting-context, 对应其中文翻译就是块级格式上下文,它是一个独立的渲染区域,我们可以把BFC理解为,一个封闭的容器,内部的元素无论怎么变化都不会影响到外部,容器内的样式布局自然也不会受到外界的影响。
【BFC内部规则】1BFC它就是一个块级元素,块级元素会在垂直方向上一个接一个的往下排列,2BFC就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签,3BFC区域不会与浮动的容器发生重叠,4属于同一个BFC的两个相邻元素的外边距会发生重叠,垂直方向的距离由两个元素中margin的较大的值决定,5计算BFC的高度时,浮动元素也会参与计算。
【如何触发BFC? 】通过添加CSS属性,就可以触发,overflow:hidden;除了visible以外的值,position:absolute/fixed;display:inline-block/flex;

【BFC到底解决什么问题】?

它可以阻止元素被浮动元素覆盖,例如,一个两栏布局,左边div宽度固定,同时设置左浮动,右边的div自适应,此时由于浮动元素脱离文档流了,不占据空间,那么就会导致右侧的div到了最左边,同时左侧浮动的div还会覆盖在上面,这时候我们就可以通过把右侧的div元素设置为一个BFC,比如可以给它添加display:flex;属性来触发,就可以解决右侧被左侧覆盖的问题。能够解决父元素没有高度,子元素设置成浮动元素时,产生父元素高度塌陷问题,比如一个容器内的两个div都是浮动元素,此时我们给父元素添加一个红色的背景色,会发现没有任何效果,因为父元素高度塌陷,高度为0,这个时候我们就可以添加一个触发BFC功能的属性,因为BFC有个规则是计算BFC高度时,浮动元素也会参与计算,所以触发BFC后,父元素的高度就会被撑开,也就是会产生清除浮动的效果。第三可以解决margin边距重叠的问题,比如一个容器里有两个div,一个div的下边距的margin设置的是10px,一个div的上边距设置的是20px,那这两个盒子之间的距离是20px,而不是30px,这就是margin塌陷问题,这个时候margin应为两个div之间较大的那个margin值,而不是两者相加,如果就想让他们之间的间距是30px,就需要触发一个div的BFC,它的内部就会遵循BFC规则,解决办法是为元素包裹一个盒子,形成一个完全独立的空间,做到里面的元素,不被外面的元素影响。

13. 伪类和伪元素的区别?

在 CSS3 中,规定了伪类用单冒号 (:)表示,伪元素用双冒号表示 (::),对于css3之前已经存在的伪元素,也是支持单冒号的,但我们在开发过程中还是要尽量规范写法。

【区别】
伪类:当我们希望样式在某些特定状态下才被呈现到指定的元素时,换句话说就是,当某个元素状态改变时,我们期待给这个元素添加一些特殊效果,那么我们就可以往元素的选择器后面加上对应的伪类。比如: hover就能够指定当我们悬浮在某元素上时,期望该元素要显示的样式。

伪元素:则是创建了一些不在文档树中的元素,并为其添加样式,需要注意的是伪元素样式里必须要给它一个content属性。比如可以通过::before伪元素在个元素前增加一些文本,并为这些文本添加样式。这些文本实际上不在文档树中的,所以叫伪元素。

总结来看,伪类的操作对象是文档树中已有的元素,而伪元素则是创建文档树以外的元素并为其添加样式。所以二者最核心区别就在于,是否创造了“新的元素"。

<template><div><div class="div1">第一个字符是红色。</div><div class="div2">选择部分背景是黑色哟。</div><div class="div3">该内容前面插入内容123。</div>链接被访问后是红色,悬浮是绿色。<div><p>第一个p元素会是黄色</p><p>第一个p元素会是黄色</p></div></div></template><style scoped>/*单冒号(是伪类,双冒号《::》是伪元素*/.div1::first-letter{color: red;}.div2::selection{background-color: black;}.div3::before{content:'123';}a:visited {color: red;}a:hover{color: limegreen;}p:first-child {color:yellow;}<style>

14. scoped的作用?

当style标签拥有scoped属性时,它的CSS样式就只作用于当前的组件。也就是说,scoped可以使得组件之间的样式互相隔离,互不影响。如果一个项目中的所有style标签全部加上了scoped,就相当于实现了样式的模块化。【原理】添加scoped编译后,会给组件内所有标签元素添加一个唯一标识,这个唯一标识就是自定义属性
data-v-xxxxxx(8个随机数)这样的字眼,同时,对应的样式选择器也会添加上这个唯一的属性选择器,由于每个组件的标识都是唯一的,意味着不会有重复的属性选择器出现,所以能够实现组件间的样式隔离。对于data-v-xxxxxx这个唯一标识,可以运行一个vue项目,f12后元素检索就能看到每个元素都带了一个这样的标识。

15. 纯css画一个三角形?

css 画三角形的原理是利用盒子边框完成的,实现步骤可以分为以下四步: 1设置一个盒子 2设置四周不同颜色的边框 3将盒子宽高设置为 0,仅保留边框 4得到四个三角形,选择其中一个三角形后,将其他三角形的边框颜色设置颜色为透明。

但这种方式,虽然视觉上是实现了三角形,但实际上,隐藏的部分任然占据部分高度,需要将上方的宽度去掉。

 .triangle {      width: 0px;      height: 0px;      border-top: 100px solid transparent;      border-bottom: 100px solid red;      border-left: 100px solid transparent;      border-right: 100px solid transparent;  }  <div class="triangle"></div>
利用伪元素画一个三角形
   #triangle {        margin: 100px;        /* width: 100px;        height: 100px; */        background-color: pink;        position: relative;    }    #triangle::before {        position: absolute;        content: "";        width: 0;        height: 0;        top: 0px;        left: 100px;        border-top: solid 50px transparent;        border-left: solid 50px pink;        border-bottom: solid 50px transparent;    }<div id='triangle'></div>

二、js面试题

1. 节流和防抖?

防抖,就是在连续的操作中,无论进行了多长时间,只有某一次的操作后,在指定的时间内没有再操作,这一次才被判定为有效。(事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。)防抖的应用场景:搜索框输入关键字的过程中,实时请求服务器匹配搜索结果,如果不进行处理,输入框内容一直发生变化,导致一直发送请求。如果进行防抖处理,结果就是,当我们输入内容完成后,一定时间内(比如500ms),没有再输入内容,这时再触发请求。
// 1. 利用计时器,实现防抖。// 2. 通过 setTimeout 的方式,在一定的时间间隔内,将多次触发变成一次触发。function debounce(fn, delay) {    let timer;    return function () {        let args = arguments;        if (timer) {            clearTimeout(timer);        }        timer = setTimeout(()=> {            fn.apply(this, args);         }, delay);    };}
节流的意思是,在频繁的操作中,在规定时间内,只触发一次。比如我们设定500ms,在这个时间内,无论点击按钮多少次,它都只会触发一次。节流的应用场景:1.抢购的时候,无数人快速的点击按钮,如果每次点击都发送请求,就会给服务器造成巨大的压力,但是我们进行节流后,就会大大减少请求的次数。2.防止表单提交按钮被多次触发,我们应该选择使用节流而不是防抖。3. 滚动加载,加载更多或滚到底部监听。
function throttle(fn, delay) {    let timer;    return function () {        let _this = this;        let args = arguments;        if (timer) {            return;        }        timer = setTimeout(function () {            fn.apply(_this, args);            // 在delay后执行完fn之后清空timer,此时timer为假,throttle触发可以进入计时器            timer = null;        }, delay)    }}

2. 内存泄漏如何处理?

内存泄漏就是指由于疏忽或者程序的某些错误造成未能释放已经不再使用的内存的情况。简单来讲就是假设某个变量占用100M的内存,而你又用不到这个变量,但是这个变量没有被手动的回收或自动回收,仍然占100M的内存空间,这就是一种内存的浪费,就是内存泄漏。(内存泄漏是指在程序运行时,申请的内存空间没有被及时释放,导致程序占用的内存不断增加,最终可能导致系统崩溃或变得非常缓慢。)在前端开发中,内存泄漏可能会发生在 js代码中。例如,在使用闭包时,如果闭包中引用了外部函数的变量,而这个外部函数已经执行完毕,但是闭包依然存在,那么这个外部函数占用的内存就无法被释放,可能导致内存泄漏。为了避免内存泄漏,开发人员应该及时释放不再使用的变量和资源,避免出现循环引用等情况,并使用一些工具和技术来检测和解决内存泄漏问题。
(1) 绑在 EventBus 的事件没有解绑
(2)全局变量造成的内存泄露,声明的全局变量在切换页面的时候没有清空。
(3)遗忘的定时器
(4)控制台的打印
(5)闭包使用不当引起内存泄漏
(6)v-if 指令产生的内存泄露

3. 谈谈你对浅拷贝和深拷贝的理解?

浅拷贝就是通过赋值的方式进行拷贝,只拷贝对象的引用,是对指针的拷贝,拷贝后两个指针指向同一个内存,同一份数据,意味着当原对象发生变化的时候,拷贝对象也跟着变化。
什么时候使用深拷贝?:后台返回数据,我们需要对数据进行操作,可能其他地方也需要使用这份数据,直接修改会导致很多其他隐性问题,使用深拷贝,就能让我们更安全,更安心的去操作这些数据了,因为反正我们复制了一份下来。
深拷贝:不但对指针进行拷贝,而且还对指针指向的内容进行拷贝,也就是另外申请了一块空间内存,内容和原对象一致,但是是两份独立的数据,更改原对象,拷贝对象不会发生变化。大白话:假设B复制了A,当修改A时,如果B也跟着变了,说明只拷贝了指针,A,B实际共用一份数据,这是浅拷贝;如果A变,B没变,那就是深拷贝,复制对象不受原对象影响。因为不仅拷贝了指针,还拷贝了内容,他们自己有自己的内存,互相独立。

深拷贝的实现方式

(1) js内置对象的JSON对象的序列化和反对象化方法:JSON.parse + JSON.stringify

// 但是这个方法是有局限的,无法实现对对象中方法的深拷贝,取不到值为undefined的key等等之类的。function cloneDeepJson(obj){  return JSON.parse(JSON.stringify(obj))}

(2) 使用递归实现深拷贝。a. 传入的原对象,遍历其属性,每个属性需要判断它的值是否是object类,如果不是,说明是基本数据类型,可以直接赋值;b. 如果是object类,那么需要再具体判断这个数据是对象还是数组,是数组创建一个空数组[],是对象则创建一个空对象{},继续递归。

//递归实现深拷贝function deepClone(origin, target){  var target = target || {};  //防止不传target  for(let key in origin){   //遍历origin中的属性    if(origin.hasOwnProperty(key)){  //判断自身是否有该属性而非原型链上的      if( origin[key] && typeof(origin[key]) == "object"){    //如果当前value是一个object类型,需要往下递归        target[key] = Array.isArray(origin[key]) ? [] : {};   //判断这个object类型,具体是数组还是对象        deepClone(origin[key], target[key]);   //递归      }else{        target[key] = origin[key];  //如果当前value不是一个object类型,直接赋值即可      }    }  }  return target;  //返回最终的拷贝对象}

(3) loadash是一个很热门的函数库,我们引入这个库后,就可以直接使用这个方法了,但是如果项目本身没有引入这个库,就不要为了使用深拷贝专门引入整个库,这样有点得不偿失。
(2) Object.assign(target,…sources) ES6新增的方法可以实现深拷贝,target: 拷贝给谁。 sources: 拷贝的对象。

// 注意:只有当对象中没有嵌套对象时,才可以实现深拷贝const foo = {    name: '张三',    age: 24}const newFoo = Object.assign({}, foo)foo.age = 25console.log(foo, newFoo) // {name: '张三', age: 25} {name: '张三', age: 24}// 对象中有内嵌的对象时const foo = {    name: '张三',    info: {        age: 24    }}const newFoo = Object.assign({}, foo)foo.info.age = 25console.log(foo, newFoo) // { name: '张三', info: { age: 25 } } { name: '张三', info: { age: 25 } }

(3) for ··· in ,使用for ··· in 遍历赋值太麻烦,不推荐。只要碰到某一个复杂数据类型(数组、对象),就再次进入这个复杂数据类型进行二次遍历,如果还有就再进入继续遍历。
(4) 通过 json 反序列化实现深拷贝。不管多复杂的数据类型,转换为json字符串以后就是基本数据类型,字符串的赋值就是基本数据类型的赋值,赋值以后再转换回来。
(6) ...展开运算符。拷贝一层是深拷贝,但是修改对象里数组的某一项就会受影响,说明是浅拷贝。

  let obj11 = {       name: "张三",       age: 18,       hobbies: ['sleep', 'eat', 'play']   }   let obj12 = { ...obj11 }   obj12.name = "胡歌"   obj12.hobbies[0] = 'sleep + doDream'   console.log('--------------------', obj11, obj12);

(7) arr.concat() 方法。只深拷贝最外层,修改数组里的对象就会受影响,是浅拷贝。

 // 该方法用于数组合并,合并的数组.concat(被合并的数组元素…) // 参数可有多个,用逗号分隔,返回合并后的数组。 // 原理:用原数组去合并一个空数组,返回合并后的新数组。 let arr11 = [1, 3, {      username: 'coco'  }];  let arr22 = arr11.concat();  arr22[2].username = 'xxxxx';  arr22[0] = 555;  // console.log(arr, 'arr');1  console.log('arr11---------------', arr11);  console.log('arr22--------------', arr22);

(8) arr.slice() 方法。只深拷贝最外层。

 const arr1 = [1, 3, { username: 'aaaaaa' }] const arr2 = arr1.slice()  //返回截取后的数组,这里没有截取掉任何项,相当于返回原数组。  // 修改堆内存中的值 arr2[0] = 5 arr2[2].username = 'bbbbbb' console.log('arr1---', arr1) console.log('arr2---', arr2)

https://zhuanlan.zhihu.com/p/556923120 深入了解深浅拷贝

4. 什么是冒泡,如何解决冒泡?什么是事件代理(事件委托)?

指父元素和子元素有相同的事件,当触发子元素事件时,会向上冒泡,同时也会触发父元素事件。

阻止冒泡:通过 event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。

不需要给每一子节点都绑定事件,而是将子节点的事件绑定到他们共同的结构父级接节点上,这样在点击每一个子节点的时候,会利用事件传播冒泡执行父节点上绑定的事件。

5. axios是什么? axios是怎么封装的?

axios是一个封装好的独立的ajax请求库, 基于Promise。支持在浏览器和Node中使用。

axios封装的步骤:

在根目录创建 utils 文件夹创建 request.js文件在request.js里引入 axios配置 基本路径和超时时间配置请求拦截和响应拦截在请求拦截里可以放 loading 和 token在响应拦截中 可以 清除 loading 还有处理错误编码字典最后把我们封装的 axios 实例 导出

6. es6的新特性用到哪些?

声明变量的的关键字:let;声明常量的const。解构赋值模板字符串箭头函数扩展运算符promise 用来封装异步操作并且可以获取成功或失败的结果。

7. 什么是闭包?优缺点是什么?

闭包指的是有权访问另外一个函数作用域中变量的函数。 优点: 1. 一个是可以读取外层函数内部的变量。2. 让这些变量始终保存在内存中,即闭包可以使它的诞生环境一直存在(延伸了变量的作用范围。)缺点: 容易造成内存泄漏。 满足以下条件才是闭包:1有函数嵌套2内部函数引用外部作用域的变量3返回值是一个函数4创建一个对象函数,让其长期驻留为什么需要使用闭包?因为全局变量容易污染环境,而局部变量又无法长期驻留内存,于是我们需要一种机制,既能长期保存变量又不会污染全局,这就是闭包。什么时候使用闭包?:当我们需要重复使用一个对象,又想保护这个对象不被其他代码污染的时候,就可以使用闭包。如何销毁闭包的内存空间?将外部接收函数的变量重新赋值为null即可。
        // 写一个函数,每次调用,数字都要减1 --------- 全局变量,占用内存,任意函数(地方)都可以随便调用,会污染环境。        var a = 100;        function fn() {            a--;            console.log(a);        }        fn()  // 99        fn()  // 98        fn()  // 97        //  -----------每次调用,结果都是99。每次函数一调用完就会销毁。每次调用,b都是100开始,存不住。        function fn2() {            var b = 100;            b--;            console.log(b);        }        fn2()  // 99        fn2()  // 99        fn2()  // 99        // ------------闭包的写法        function fn3() {            let c = 100;            function fn4() {                  c--;                console.log(c);            }            return fn4;        }        var fn5 = fn3();        fn5()   // 99        fn5()   // 98         fn5()   // 97        fn5 = null;

8. 浏览器的同源策略(跨域问题)有了解过吗?

如果一个请求url的 协议、域名、端口三者之间任意一个与当前页面url不同就会产生跨域的现象。同源策略,是一种网页的安全协议。同源:同协议,同域名,同端口。 vue-cli代理跨域:在 vue.config.js文件的devServer对象的 proxy中配置。
module.exports={pages:{index:{//入口entry:'src/main.js',},},lintOnSave:false,//关闭语法检查//开启代理服务器 方法一// devServer:{//paroxy:'http://localhost:5000',//5000服务器端口号,// },//方法二:devServer:{proxy:{// 请求前缀/api,只有加了/api前缀的请求才会走代理(前端自定义)'/api':{target:'http://localhost:5000',pathReweite:{'^/api':''},//重写//ws:true,//用于支持websocketchangeOrigin:true,//用于控制请求头中的host值,默认true,react中默认false}}}}

在使用webpack或者vite构建工具时候,配置反向代理,把本地所有的axios请求的地址,(域名:端口)改成:/api/,本地发起请求的地址 /api/ 都会被自动替换成实际后端请求地址;这样就实现了当前请求的地址,就会被后端接收识别为和前端都一样的域名;保证了前后端域名一致,解决了跨域;
2. nginx里面也可以使用同样的方式,通过反向代理转发,来解决跨域问题。
3. JSONP解决跨域。需要前后端一起配合,利用同源策略对script标签不受限制,不过只支持get请求。
4. CORS跨域资源共享。cors跨域,只需要后端配置,服务端设置header(“Access-Control-Allow-Origin:*”);允许任何来源,header(“Access-Control-Allow-Origin:http://me.com”);只允许来自域名http://me.com的请求。

9. 前端登陆流程

在登录页点击登录的时候,前端会带着用户名和密码去调用后端的登录接口。后端收到请求,验证用户名和密码,验证失败,会返回错误信息,前端提示相应错误信息,如果验证成功,就会给前端返回一个token。前端拿到token,将token储存到Vuex和localStorage中,并跳转页面,即登录成功。前端每次跳转至需要具备登录状态的页面时,都需要判断当前token是否存在,不存在就跳转到登录页,存在则正常跳转(通常封装在路由守卫中)。另外,在向后端发送其他请求时,需要在请求头中带上token(项目中通常封装在请求拦截器中),后端判断请求头中有无token,有则验证该token,验证成功就正常返回数据,验证失败(如已过期)则返回相应错误码。前端拿到错误信息,清除token并回退至登录页。

11. New操作符做了什么?

第一步创建一个空对象。第二步将 this 指向空对象。第三步动态给刚创建的对象添加成员属性。第四步隐式返回 this。

12. 箭头函数与普通函数的区别?箭头函数能当构造函数吗?

1. 箭头函数比普通函数在写法上更加简洁。    (1)如果没有参数,就直接写一个空括号即可    (2) 如果只有一个参数,可以省去参数的括号    (3)如果有多个参数,用逗号分割    (4)如果函数体的返回值只有一句,可以省略大括号2. 箭头函数没有自己的this箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。3. 箭头函数继承来的this指向永远不会改变,call()、apply()、bind()等方法不能改变箭头函数中this的指向 。var id = 'GLOBAL';var obj = {  id: 'OBJ',  a: function(){    console.log(this.id);  },    b: () => {    console.log(this.id);  }};obj.a();    // 'OBJ'obj.b();    // 'GLOBAL'new obj.a()  // undefinednew obj.b()  // Uncaught TypeError: obj.b is not a constructor对象obj的方法b是使用箭头函数定义的,这个函数中的this就永远指向它定义时所处的全局执行环境中的this,即便这个函数是作为对象obj的方法调用,this依旧指向Window对象。需要注意,定义对象的大括号{ }是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。4. 箭头函数不能作为构造函数使用 由于箭头函数时没有自己的this,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。5. 箭头函数没有自己的arguments箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。6. 箭头函数没有prototype7. 箭头函数的this指向哪⾥?箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于⾃⼰的this,它所谓的this是捕获其所在上下⽂的 this 值,作为⾃⼰的 this 值,并且由于没有属于⾃⼰的this,所以是不会被new调⽤的,这个所谓的this也不会被改变。

13. 什么是伪数组(又叫对象数组)?它和数组有什么区别?

伪数组数据类型是object对象,而真实的数组数据类型 array。伪数组有length属性,索引,可以通过.length获取长度,并且可以通过索引获取某个元素,但没有forEach等方法,而数组拥有数组全部方法。伪数组长度不可以改变,数组长度可以改变。伪数组因为是对象数组所以用 for… in遍历,数组更建议用for … of。伪数组转数组用 array.from或直接展开运算符展开在一个新数组里。在JavaScript中常见的伪数组就是arguments,函数的参数 arguments;另外还有,原生JS获取dom元素,document.querySelector(“div”) 获取到的列表也是一个伪数组。

14. 怎么把类数组转换为数组?

通过Array.from方法来实现转换
Array.from(arrayLike)通过call调用数组的slice方法来实现转换
Array.prototype.slice.call(arrayLike)通过call调用数组的splice方法来实现转换
Array.prototype.splice.call(arrayLike,0)通过apply调用数组的concat方法来实现转换
Array.prototype.concat.apply([],arrayLike)

15. 说一下常见的检测数据类型的几种方式?

typeof 其中数组、对象、null都会被判断为Object,其他判断都正确。instanceof 只能判断引用数据类型,不能判断基本数据类型。constructor 它有两个作用 ,一是判断数据的类型,二是对象实例通过constructor对象访问它的构造函数。需要注意的事情是如果创建一个对象来改变它的原型,constructor就不能来判断数据类型了。Object.prototype.toString.call() 使用 object 对象的原型方法 tostring 来判断数据类型。

instanceof和typeof的区别:
instanceof:返回值为布尔值。instanceof 用于判断一个变量是否属于某个对象的实例。
typeof:返回值是一个字符串, 用来说明变量的数据类型。typeof 一般只能返回如下几个结果:number,boolean, string, function, object, undefined

16. 数组去重有几种方法?

1.最简单的set去重var aa=[23,45,23,23,34,2,34,66,78];var aa=new set(aa);console.log(aa);2.用indexOf()来去重var sum=[ ]; //给它一个空的数组for(var i=o;i<aa.length;i++){//如果没有找到字符串,则返回-1,检索是否存在。if(sum.indexOf(aa[i]>=0){continue;//结束本次循环}else{sum.push(aa[i]);}}console.log(sum);3.用sort排序后去重function fn(arr){ let newArr = [] arr.sort((a,b)=>{       return a-b }) arr.forEach((val,index)=>{     if(val != arr[index+1]){          newArr.push(val)     }   }) return newArr;}4.数组去重,根据里面相同的ID去重,键值var str=[        {name:"张三",id:1},        {name:"李四",id:2},        {name:"王五",id:2},        {name:"小明",id:3},        {name:"小兰",id:1},    ];//声明一个数组var result=[];//声明一个对象var obj={};for(var i=0;i<str.length;i++){if(!obj[str[i].id]){result.push(str[i]);obj[str[i].id]=true;}}console.log(result);5.普通去重的方式function removeRepeat(arr) {for(var i=0;i<arr.length;i++){        for(var j=i+1;j<arr.length;j++){            if(arr[i]==arr[j]){                arr.splice(j,1);  //索引,删除长度                j--;             }        }    }console.log(arr); return arr;}removeRepeat([23, 45, 23, 23, 34, 2, 34, 66, 78])

17. 如何判断一个对象是空对象?

// 1.使用JSON自带的.stringify 方法来判断 if(Json.stringify(obj)=='{}'){console.log('是空对象') }// 2.使用ES6新增的方法Object.keys()来判断 if(Object.keys(Obj).length<0){console.log('是空对象') }

18. for…in 和 for…of的区别?

for…in 遍历对象获取的是对象的键名,for…in 遍历数组获取的是数组的索引值。for…of 遍历对象报错:obj is not iterable,for…of遍历数组得到的是数组的每一项值。for…in会遍历对象的整个原型链,性能非常差不推荐使用。而for…of 只遍历当前对象不会遍历原型链。对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值。

总结:for…in循环主要是为了遍历对象而生,不适用遍历数组。 for…of 循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象。

19. JSON.stringify有什么缺点?

1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将是字符串的形式,而不是对象的形式。
2. 如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象。
3. 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失。
4. 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null。
5. JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor。
6. 如果对象中存在循环引用的情况也无法正确实现深拷贝。

20. Set的用法?

一、Set数据结构的特点?
ES6提供了新的数据结构Set,它类似于数组,但是成员的值都是唯一的,没有重复的值。Set不同于Map,它没有键值对的概念,它只有一个维度,就是value。
二、Set数据结构应用在哪些地方?
应用:搜索历史关键字的存储 网站的搜索功能,用户搜索完成后,网站要记录用户搜索的关键字,方便用户下次直接点击,搜索历史关键字就可以了。用户搜索历史关键字,不能有重复的值,当用户多次输入相同的关键字,用set存储值,内部会自动判断值是否重复,如果重复,就不会再存储。
三、Set的用法
Set本身是一个构造函数,用来生成Set数据结构。属性size,Set里面有多少个元素,类似于数组的length。
const set = new Set();const s1 = new Set();  // 空的Set数据结构console.log(s1.size)  // 0 const s2 = new Set(["a", "b"]);console.log(s2.size)  // 2
Set函数可以接受一个数组作为参数,用来初始化。
1. const set = new Set([1,2,3,4,5,4,5]);console.log(set)  // {1, 2, 3, 4, 5}2. const s3 = new Set(["a","a","b","b"]);console.log(s3.size)  // 2const ary = [...s3]; console.log(ary)  // ["a","b"]
四、Set的属性和方法(实例方法)
1. add(value): 添加某个值,返回Set结构本身。const s4 = new Set();// 向set结构中添加值 使用add方法s4.add('a').add('b');  // 链式调用console.log(s4.size)  // 2 2. delete(value): 删除某个值,返回一个布尔值,表示删除是否成功。const s4 = new Set(['1','2','c']);const r1 = s4.delete('c');console.log(s4.size)  // 2console.log(r1);    // true3. has(value): 返回一个布尔值,表示该值是否为Set的成员。const s4 = new Set(['1','2','c']);const r2 = s4.has('d');console.log(r2)  // false4. clear(): 清除所有成员,没有返回值。const s4 = new Set(['1','2','c']);s4.clear();console.log(s4.size);  // 0 5. 遍历 (方法)- Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。// 遍历set数据结构,从中取值const s5 = new Set(['a', 'b', 'c']);s5.forEach(value => {console.log(value)  })// a   b   c

21. Map的用法

一、Map数据结构是用来干嘛的?
Map对象用来存储键值对。Map本身是一个构造函数,用来生成Map数据结构。
// 创建一个Map const map = new Map();
二、Map的属性和方法(实例方法)
1.  size属性- 返回Map对象中所包含的键值对的个数。const map = new Map()const a = map.set(1,'one')console.log(a.size)  // 12.  set方法- 向Map中添加新元素。- 第一次set是添加数据,第二次添加相同的key时,会将第一次添加的值给覆盖掉。set(key,value)const map = new Map()const a = map.set(1,'one')console.log(a)    // {1 => 'one'}3.  get方法- 通过键,查找特定的值。get(key)4.  delete方法- 通过键,从Map中移除对应的数据。delete(key)5.  has方法- 判断Map对象中是否存在key,若有则返回true,否则返回false。has(key)6.  clear方法- 将这个Map中的所有元素删除。clear(key)
三、如何遍历Map
1. keys(): 返回键名的遍历器const map = new Map()map.set(1,'one')map.set(2,'two')map.set(3,'three')for(const item of map.keys()) {console.log(item);  // 1 2 3}2. values(): 返回键值的遍历器const map = new Map()map.set(1,'one')map.set(2,'two')map.set(3,'three')for(const item of map.values()) {console.log(item);  // 'one' 'two' 'three'}3. entries(): 返回键值对的遍历器- entry条目的意思,entries复数,意思指键值对。- entries方法中的键值对是以数组的形式存在的。const map = new Map()map.set(1,'one')map.set(2,'two')map.set(3,'three')for(const item of map.entries()) {console.log(item);  // 返回三个数组[1,'one'] , [2,'two'], [3,'three']}4. forEach(): 使用回调函数遍历每个成员第一个参数:item,是Map的value值第二个参数:index 是Map的key值第三个参数: Map本身

22. js的数据类型

is中数据类型分为基本数据类型和引用数据类型。基本数据类型有Number,String,Boolean,Null,Undefined,es6新增的Symbol以及es10新增的Biglnt(任意精度整数)七类。引用数据类型即obiect类,比如对象数组,以及函数。 存储位置:基本类型存储在栈内存中而引用数据类型在栈内存中存的是对象指针,这个指针指向堆内存中的值,也就是说实际值是存在堆内存中,栈中存的是对象在堆内存中的引用地址,通过这个引用地址可以找到保存在堆内存中的对象。如果两个变量保存的是同一个地址值,说明指向的是同一份数据,当一个变量修改属性时,另一个也必然会受到影响。

三、网络

1. get请求和post请求的区别?

get请求的参数是拼接在地址栏中,隐私性和安全性都比较差,请求的数据长度是有限制的,
不同的浏览器和服务器不同,一般限制在 2~8K 之间,更加常见的是 1k 以内;post请求是没有的长度限制的,请求数据是放在body中。get请求一般是去取获取数据(也可以提交,但常见的是获取数据);post请求一般是去提交数据。get请求刷新服务器或者回退没有影响,post请求回退时会重新提交数据请求。get请求可以被缓存,post请求不会被缓存。get请求会被保存在浏览器历史记录当中,post不会。get请求可以被收藏为书签,因为参数就在url中,但post不能。post请求的参数不在url中。get请求只能进行url编码(appliacation-x-www-form-urlencoded),post请求支持多种(multipart/form-data等)。

2. get和post是否使用缓存?

get和post是http协议中两种异曲同工的请求方式,http协议是基于TCP/IP的应用层协议,所以无论是get还是post用的都是同一个传输层协议,所以在传输上可以认为基本无差。 get请求类似于查找的过程,通常是向服务器获取数据时使用,比如查询,get请求如果带参数,参数会拼接在地址栏的url中暴露出来,http缓存通常只适用于不改变服务端数据的请求,所以get符合http缓存,适用于不改变服务端请求数据的这个原则,所以说get请求可以被缓存。post不同,post一般向服务器提交数据时使用,做的是修改和删除工作,比如添加和修改表单,post参数会放在请求体中,所以必须与数据库交互,所以不能使用缓存。

3. 阿帕奇和Nginx如何配置?

阿帕奇(Apache)和 Nginx 都是常用的 Web 服务器软件,它们可以用来处理 HTTP 请求并向客户端发送响应。下面是它们的配置方法简述:阿帕奇配置:1. 修改 Apache 的配置文件 httpd.conf(或 .htaccess 文件),配置虚拟主机和监听端口等参数。2. 配置 Apache 的模块,如 PHP 模块,通过修改 httpd.conf 文件或添加 .conf 文件。3. 重启 Apache 服务器,使配置生效。Nginx 配置:1. 修改 Nginx 的配置文件 nginx.conf,配置虚拟主机、监听端口、反向代理等参数。2. 配置 Nginx 的模块,如 Lua 模块、gzip 模块等,通过在 nginx.conf 文件中添加配置项。3. 重启 Nginx 服务器,使配置生效。需要注意的是,在配置阿帕奇和 Nginx 的过程中,需要根据实际情况选择不同的配置选项,并确保配置文件的正确性和安全性。另外,由于 Nginx 的性能优于阿帕奇,因此在高并发的情况下,可以考虑使用 Nginx 作为 Web 服务器。

4. 前后端交互?

前后端交互是指前端页面和后端服务器之间的数据传输和通信。前端通常使用 JavaScript 发送 HTTP 请求(如 GET、POST 请求等),后端服务器接收请求并处理请求,返回相应的数据给前端。下面是前后端交互的基本流程:1. 前端页面通过 JavaScript 构造 HTTP 请求,包括请求方法(GET、POST 等)、请求 URL、请求头、请求体等。发送 HTTP 请求到后端服务器。可以使用浏览器的 XMLHttpRequest 对象、Fetch API,或者第三方库(如 axios)发送请求。2. 后端服务器接收请求,根据请求 URL 和请求方法进行处理,并从数据库或其他资源中获取数据,对数据进行处理和计算。后端服务器将处理后的数据封装成 HTTP 响应,包括响应状态码、响应头、响应体等。3. 前端页面接收到 HTTP 响应,解析响应数据,并根据数据更新页面内容。4. 需要注意的是,前后端交互需要遵循一定的协议和规范,例如 HTTP 协议、JSON 数据格式等。此外,为了提高交互效率和用户体验,可以使用一些技术和工具,如 Ajax、WebSocket、RESTful API 等。

5. 前后端如何联调?

前端和后端分离,由不同人员负责开发的时候,在开发过程中前后端联调进行数据对接与调试就是开发中经常会遇到的事情。我们平时练习前端开发的时候,是直接提供接口文档和服务器地址,然后我们直接在webpack中配置就可以请求到数据了。但是在实际开发中,服务器地址有了,但是后端的接口还没有写完,还没有部署到共用的服务器上。 开发前,和后端约定好接口协议,数据格式。(前端需要显示的数据内容和格式一般json比较多)开发中,前端通过mock数据,完成本地开发和ui渲染联调:后端给前端接口文档,前端可以通过postman调试,也可以把接口放在前端项目中调试,请求返回的接口数据有疑问就继续让后端调整就行。

通俗说就是前端去调后端的接口,把数据传给后端或者从后端那边取数据。作为前端,我们主要关注的是接口的地址是什么,前端调接口的时候要传什么参数给后端,后端又会返回什么样的数据给我们等等。这些信息我们都会从接口文档上找到答案, 接口文档一般由后端按规范编写后提供给我们,在开发中,如果发现实际收到的数据和接口文档上不一致,那么这时候就可以和后端进行沟通,前端不能擅自修改,如果有的数据格式,不是我们想要的无非也就两种情况,要么是和后端老师沟通让后台老师改,要么前端自己把数据处理成理想的状态。

6. 浏览器的缓存机制?

为什么需要浏览器缓存?1. 浏览器的缓存,主要针对的是前端的静态资源。最好的效果是,我们在发起请求后,拉去相应的静态资源,并且保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,就直接从本地读取;如果服务器的静态资源已经更新,那么我们再次请求的时候,就到服务器拉取新的资源,并保存在本地。这样能减少请求的次数,提高网站的性能。2. 所谓的浏览器缓存:指的是浏览器将用户请求过的静态资源,存储到电脑本地磁盘中,当浏览器再次访问的时候,就可以直接从本地加载,不需要再去服务端请求了。3. 使用浏览器缓存,有以下优点:     (1)减少服务器的负担,提高了网站的性能。     (2)加快客户端请求网页的加载速度。     (3)减少了多余网络数据传输。

7. 在地址栏里输入一个地址回车会发生那些事情?

(1) 解析URL
(2) 缓存判断
(3) DNS解析
(4) 获取MAC地址
(5) TCP三次握手
(6) HTTPS握手
(7) 返回数据
(8) 页面渲染
(9) TCP四次挥手

8. HTTP和HTTPS协议的区别是什么?

http,也就是超文本传输协议,是互联网上应用最为广泛的一种传输协议,它是以明文方式发送信息的,所以如果有不法分子截取了Web浏览器和服务器之间的传输报文,就可以直接获得信息,可想而知这样是不安全。https可以认为是http的安全版,是在HTTP的基础上加入了SSL协议,https中的s就指的是SSL协议,这一层协议加在了传输层和应用层之间,它可以对通信数据进行加密,且能够验证身份,使得通信更加安全。当然不是绝对安全,只是会增加不法分子的破坏成本。https和http的区别:
(1)二者的连接方式不同,https的端口是443,而http的端口是80。
(2)http传输是明文的,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。而https是用ssl进行加密的,安全性更高。
(3)https要申请ca证书,一般免费证书较少,所以需要一定的费用,而http不需。
(4) http协议速度比https更快,因为较https而言,它不需要经过复杂的ssl握手。而https协议需要经过一些复杂的安全过程,页面响应速度会来得慢。

9. tcp和udp的区别及应用场景

tcp和udp两者都是通信协议,也都是传输层协议,但是他们的通信机制和应用场景不同。

udp是无连接的(发送数据前不需要建立连接)tcp是面向连接的(需要建立连接后传输数据,这里说的就是tcp的三次

点击全文阅读


本文链接:http://zhangshiyu.com/post/59174.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1