CSS 与 HTML 部分
什么是盒子模型?
在网页中,一个元素占有空间的大小由四个部分组成,分别是:元素的内容 content、元素的边距 padding 、元素的边框 border 和元素的外边距 margin,此四部分一起构成了 CSS 中元素的盒子模型。
常见行内元素有哪些?块级元素有哪些?空元素有哪些?
行内元素:a、b、span、img、input、strong、select、label、em、button、textarea
块级元素:div、ul、li、dl、dt、dd、p、h1~h6、blockquote
空元素即没有内容的元素:br、meta、hr、link、input、img
CSS实现盒子的垂直水平居中(最少 3 种方式)
1.flex 布局:(给父盒子添加以下三行 CSS 代码即可)
display: fiex;
/* 让盒子在主轴上居中,主轴默认X轴即水平方向 */
justify-content: center;
/* 让盒子在侧轴上居中(适合子元素为单个时使用),侧轴默认Y轴即垂直方向 */
align-items: center;
2.使用定位——子绝父相
给父元素添加:相对定位
position: relative;
给需要居中的元素自身添加绝对定位:然后距离上和左50%的距离,再向上向左移动盒子自身的一半(向上和向左都是负值)。
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
注意:父元素和元素自身都需要给定高宽。
3.还是定位——子绝父相
给父元素添加:相对定位
position: relative;
给需要居中的元素自身添加绝对定位:然后给定 top、left、right、bottom 的值都为 0,最后给定 margin: auto;即可
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
flex-flow 表示什么?
flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写形式,默认值为 row nowrap 即从左到右水平排列内部项且不换行。
justify-content 属性值中的 space-between 和 space-around 区别是什么?
justify-content 用于定义项目在主轴上的对齐方式。值 space-between 表示两端对齐,项目之间的间隔都相等,默认两端会贴边显示。值 space-around 表示每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍,两端的项目不会贴边显示。
flex: 1; 表示什么?
flex 属性是 flex-grow, flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。后两个属性可选。
flex: 1; 表示该项会和其他相同值的项等分父元素的剩余空间。
简述一下 src 与 href 的区别
href 是指向网络资源所在位置,建立和当前元素/锚点或链接之间的链接,用于超链接;
src 是指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求 src 资源时会将其指向的资源下载并应用到文档内,例如 JS 脚本、img 图片等元素。当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕。图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么建议将 JS 脚本放在文档底部而不是头部。
px 和 rem 的区别
px 和 rem 都是长度单位, px 即像素,它的值是固定的,给多少就是多少,计算比较容易。而 rem 是灵活、 可扩展的单位,由浏览器转换为像素值,具体值取决于网页设计中的字体大小。浏览器的默认字体大小是16 px 所以未经调整的浏览器都符合 1rem = 16px 的单位转换规则。
em 和 rem 的区别
rem 单位转换为 px 时取决于页面根节点即 html 节点的字体大小,转换后的 px 值 = 根元素字体大小 * 当前元素的 rem 值。例如,根元素的字体大小 16px,10rem = 160px,即 10 x 16 = 160。
em 单位转换为 px 时取决于该元素的当前的字体大小,转换后的 px 值 = 元素当前字体大小 * 元素当前的 em 值。例如,如果一个 div 的 font-size: 10px;,则 10em 将会被转换为 100px,即 10 × 10 = 100。
注意:em 转换为 px 时首先是根据元素自身的字体大小进行的,因为元素的字体大小会继承父元素的字体大小所以有个误区认为 em 是根据父元素的字体大小进行 px 转换的。
优雅降级和渐进增强是什么?二者的区别是什么?
渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级:一开始就构建完善的功能,然后再针对低版本浏览器进行兼容。
二者的区别是:
优雅降级是从复杂的现状开始,并试图减少用户体验的供给;
渐进增强则是从一个非常基础的版本开始,不断扩充,以适应未来环境的需要;
降级意味着往回看;而增强则意味着朝前看,同时保证其根基处于安全地带。
使用CSS实现一个三角形
- 等边三角形
.box {
/* 高宽一定要为 0 */
width: 0;
height: 0;
/* 行高和字体设置为0是为了兼容浏览器的 */
line-height: 0;
font-size: 0;
/* 先让盒子的四条边都为透明色,然后再给定其中一条边一个颜色即可 */
border: 50px solid transparent;
border-left-color: red; /* 顶点 朝右 的三角形 */
border-top-color: blue; /* 顶点 朝下 的三角形 */
border-bottom-color: yellow; /* 顶点 朝上 的三角形 */
border-right-color: tomato; /* 顶点 朝左 的三角形 */
}
效果图:等边三角形: 直角三角形:
- 直角三角形(原理就是相邻的两个等边三角形拼起来其实就是一个直角三角形了)
.box {
/* 高宽一定要为 0 */
width: 0px;
height: 0px;
/* 行高和字体设置为0是为了兼容浏览器的 */
line-height: 0;
font-size: 0;
/* 先让盒子的四条边都为透明色,然后再给定其中一条边一个颜色即可 */
border: 50px solid transparent;
border-left-color: red; /* 顶点 朝右 的三角形 */
/* 把顶点朝右和朝上的两个拼一起给一样的颜色即可 */
border-bottom-color: red; /* 顶点 朝上 的三角形 */
}
CSS怎么调整元素透明度
1.使用 rgba 填充颜色值,第四个数即控制色彩透明度。如:
background-color: rgba(255, 0, 0, .5);
2.使用 opacity 属性调整元素透明度,值 0~1 ,0 为完全透明,1为完全不透明。如:
.box {
width: 100px;
height: 100px;
/* background-color: rgba(255, 0, 0, .5); */
/* 上面行 rgba 调整透明度的写法等价于下面两行 */
background-color: red;
opacity: .5; /* opacity 用于调整元素透明度,值 0~1 ,0 为完全透明,1为完全不透明*/
border-radius: 50%;
}
注:rgba 调整元素透明度时该值也是 0~1 之间,0 为完全透明,1 为完全不透明。
使用CSS实现限制文本内容长度,超出部分使用省略号(...)替代的效果
.text {
width: 16em; /* 默认只显示16个字符(包含...算一个字符)*/
line-height: 30px;
background-color: #ccc;
overflow: hidden; /* 溢出部分隐藏 */
white-space: nowrap; /* 强制文本一行显示 */
text-overflow: ellipsis; /* 溢出部分使用 ... 替代 */
}
注:这样限制了文本显示之后一般还会问那需要鼠标悬停在文本上显示完整内容怎么做,其实很简单就是给该元素添加一个 title 属性值是完整内容即可。
效果图:
JavaScript 部分:
简述同步和异步的区别
js 的执行模式分为两种:同步和异步
同步是阻塞模式即后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;
异步是非阻塞模式即每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
setTimeout 和 setInterval 的区别?
setTimeout() 方法是在等待指定时间后执行函数, 且默认只执行一次传入的函数;
setInterval() 方法是每间隔指定时间后执行一次传入的函数,循环执行直至关闭窗口或clearInterval() 清除定时器。
怎样创建、添加、删除、移动、复制和查找节点?
创建元素:document.createElement()
添加元素:父元素.appendChild() // 在父元素内部最后追加的形式
插入元素:父元素.insertBefore(插入的元素, 父元素.children[0]); // 表示在父元素内部最前面插入所创建的元素
删除元素:父元素.removeChild('要删除的元素')
替换元素:replaceChild() 方法可将某个子节点替换为另一个。该方法有两个参数,参数1:替换后的值,参数2:被替换的值
查找元素:
getElementsByTagName() // 通过标签名称查找
getElementsByName() // 通过元素的 name 属性值匹配
getElementById() // 通过元素 id 值匹配
querySelector() // 可通过元素的CSS选择器匹配元素只返回匹配到的第一个元素
querySelectorAll() // 可通过元素的CSS选择器匹配元素返回匹配到的所有元素的列表
跨域是由哪几种方式实现的(什么是跨域,限制了什么,不同源的话会进行什么策略)
跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 JavaScript 施加的安全限制。
同源就是指,域名,协议,端口均相同。两个网域若 域名、协议、端口 任一不同则二者的通信就出现了跨域问题
常见的跨域解决方案有:
- JSONP
jsonp 的原理就是利用 <script> 标签没有跨域限制,通过 <script> 标签 src 属性,发送带有 callback 参数的 GET 请求,服务端将接口返回数据拼凑到 callback 函数中,返回给浏览器,浏览器解析执行,从而前端拿到 callback 函数返回的数据。
jsonp 的缺点是只支持 GET 请求。
- 中间服务器代理
注:跨域问题只存在于浏览器中,服务器端没有跨域的概念。中间服务器代理的思路就是我们前端可以不跨域的去访问一个中间服务器(这个中间服务器和我们前端网域的 域名、协议、端口都相同,则不跨域),再让中间服务器去替我们访问这个我们本来需要跨域访问的网站,中间服务器拿到我们需要的资源然后再返还给我们即可。
- CORS 跨域资源共享
CORS 是一个 W3C 标准,全称是"跨域资源共享"。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
简单来说实现 CORS 跨域资源共享其实就是在发起请求前或在服务器端进行请求头、请求类型等配置,符合配置项条件的请求就可以正常与该服务器进行通信。
Access-Control-Allow-Origin 是必须的配置项,它的值要么是请求时 Origin 字段的值(包含:协议、域名、端口),要么是一个 * ,表示接受任意域名的请求。
CORS 跨域资源共享可以在前端或后端进行配置,是目前常用的解决浏览器跨域的方案,较之 jsonp 其更加强大,几乎支持所有的请求类型。
- 配置反向代理实现跨域的方法
配置反向代理 是前端自行在项目中进行配置来解决跨域的一种方式,主要就是在项目中设置代理,开启代理就会在本地创建一个虚拟服务器,我们会通过它来发送请求,它也会把请求返回的数据返还给我们。主要就是使用中间服务器代理的思路。
比如在 Vue 项目中进行配置:CLI 2.X 创建的项目则打开项目的 config/index.js 文件,找到 dev 中的 proxyTable: {}, 字段进行配置,如果是 CLI 3.X 创建的项目则需要手动在根目录创建一个 vue.config.js 文件,在该文件中的 devServer 字段中配置 proxy: {}, 字段即可。主要配置项示例如下:(注:proxyTable 和 proxy 就是一个指同一字段,配置项写到其内部)
proxy: {
// 需要代理的接口,当地址中有 /api 时就会触发代理机制
'/api': {
target: 'http://39.97.33.178', // 目标服务器域名
ws: true, // 是否启用 websockets,就开启即可
changeOrigin: true, // 是否跨域,需要设置此值为 true 才可以让本地服务器代理我们发出请求
pathRewrite: {
'^/api': '' // 重定向,即服务器端是否需要将匹配到的值替换成其他值来进行数据请求,假设我们想把 localhost:8888/api/login 变成 http://39.97.33.178/login 就需要这么做
}
}
},
实现一个函数 clone ,可以对 JavaScript 中5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制
直通博客 -> 实现一个克隆函数,可以深拷贝JS中的5种数据类型
向数组末尾添加一个或多个数可以使用什么方法?
1.数组的 push() 方法,用于向数组末尾追加一个或多个值。示例如下:
var arr = ['a', 'b', 'c']
arr.push('d', 'e', 'f')
console.log(arr); // (6) ['a', 'b', 'c', 'd', 'e', 'f']
2.数组的 splice() 方法 ,要求在末尾追加则方法内参数1为数组的长度(length),参数2为 0,参数3为要插入的值,示例如下:
var arr = ['a', 'b', 'c']
arr.splice(arr.length , 0, 'D', 'E') // 这样写相当于push()方法即在数组末尾追加一个数(注:一次可以追加多个)
console.log(arr); // (5) ['a', 'b', 'c', 'D', 'E']
向数组中指定位置插入一个或多个数使用什么方法?
数组的 splice() 方法用于向数组中 添加、删除或替换 项目,然后返回被删除的项目。
- 添加数:注:可以一次性添加多个值,如果只添加没有删除任何值则该方法的返回值为 []
var arr = ['a', 'b', 'c']
const del = arr.splice(0, 0, 'A') // 表示从索引为 0 开始,不删除数并在该位置添加一个"A"
console.log(arr, del) // ['A', 'a', 'b', 'c'] []
- 删除数:注:参数 1 表示从哪个下标开始删除(包含该下标位置),参数 2 表示删除个数(不写默认表示一直删除到数组最后)
var arr = ['a', 'b', 'c']
const del = arr.splice(2, 1) // 表示从索引为 2 开始,删除 1 个数
console.log(arr, del); // (2) ['a', 'b'] ['c']
- 替换值:(原理就是删除一个并添加一个结合起来效果就相当于替换了一个值)
var arr = ['a', 'b', 'c']
const del = arr.splice(1, 1, 'B') // 表示从索引为 1 开始,删除 1 个数,并在该位置添加一个"B"
console.log(arr, del) // ['a', 'B', 'c'] ['b']
注:splice() 方法相当于可以操作数组的 增删改 这三大需求,所以是数组中必会的一个方法!!
如何把一个数组转换为一个字符串?
使用数组的 join() 方法,可以指定分隔每项的分隔符。示例如下:
var arr = ['a', 'b', 'c']
let str = arr.join(',') // 表示将该数组每项以","分隔转换为一个字符串
console.log(str); // a,b,c
let str2 = arr.join('') // 表示将该数组每项没有分隔符转换为一个字符串
console.log(str2); // abc
数组去重如何实现?(有几种方法)
方法一:使用 forEach() 循环遍历需要去重的数组,循环内部使用一个空数组来 indexOf() 循环的每一项,如果值为 -1 则将该项 push() 到空数组中。
var arr = [1, 2, 3, 3, 4, 5, 4, 2, 1, 4, 6, 3] // 需要去重的数组
var arr1 = [] // 空数组由 arr 去重后的数据组成
arr.forEach(item => { // forEach 循环遍历数组
// 通过判断将不在 arr1 数组中的项添加到数组中
if (arr1.indexOf(item) === -1) arr1.push(item)
})
console.log(arr1) // (6) [1, 2, 3, 4, 5, 6]
方法二:使用 ES6 中的 Set 数据类型,Set 数据结构中存储的数据不会重复,利用这一特性即可轻松实现数组去重。示例如下:
var arr = [1, 2, 4, 5, 3, 5, 3, 2, 4, 1]
var s = Array.from(new Set(arr)); // Set 中的数据不会重复
console.log(s); // (5) [1, 2, 4, 5, 3]
方法三:使用数组的 filter() 和 includes() 方法的结合实现数组去重:。示例如下:
var arr = [1, 2, 4, 5, 3, 5, 3, 2, 4, 1]
var arr2 = []
arr.filter(item => {
if(!arr2.includes(item)) {
arr2.push(item)
}
})
console.log(arr2); // (5) [1, 2, 4, 5, 3]
怎么实现一个对页面某个节点的拖拽效果?(原生JS)
掌握核心事件:onmousedown 鼠标按下事件、onmousemove 鼠标移动事件、onmouseup 鼠标松开事件、 onmouseleave 指针离开被选元素时才触发此事件、onmouseout 鼠标指针离开被选元素或任意子元素时都会被触发。
// 定义一个控制鼠标移动事件是否执行的变量
var tag = false
// 定义一个节流阀
var timer = null
// 获取到页面中我们要控制的元素
const box = document.querySelector('.box')
// 监听 box 的鼠标按下事件
box.addEventListener('mousedown', function (e) {
console.log('按下' + e.screenX, e.screenY);
// 鼠标按下后才执行鼠标移动的事件
tag = true
})
// 监听鼠标移动事件
box.addEventListener('mousemove', function (e) {
// tag 为 true 时表示鼠标在 box 中按下了,此时才执行移动事件
if (!tag) return
// 判断节流阀的值,看是否符合向下执行的条件
if(timer) return // timer 为空则向下执行
timer = setTimeout(() => {
let x = e.clientX
let y = e.clientY
// 注意要设置该元素的 position: absolute; 绝对定位才有效
box.style.top = y - 30 + 'px'
box.style.left = x - 30 + 'px'
console.log('ok');
timer = null
}, 10);
})
// 监听 box 的鼠标松开事件
box.onmouseup = function (e) {
console.log('松开' + e.screenX, e.screenY);
// 鼠标松开后就不需要执行鼠标移动事件了
tag = false
}
// 监听鼠标离开 box 的事件
box.onmouseleave = function() {
// 鼠标离开 box 后就不需要再执行鼠标移动事件了
tag = false
}
在 JavaScript 中什么是伪数组?如何将伪数组转化为标准数组?
伪数组:是无法直接调用数组方法但具有 length 属性且按索引方式存储数据的数组或对象。典型的是函数的 arguments 对象,还有像调用 getElementByTagName() 、document.childNodes 之类的,它们返回的 NodeList 数组或对象都属于伪数组。可以使用 Array.prototype.slice.call() 、 [] .slice.call() 和 ES6 中的 Array.from() 以及展开运算符 [...arr] 方法将伪数组转化为标准数组。示例如下:
<script>
var arr = document.querySelectorAll('div')
console.log(Object.prototype.toString.call(arr));
console.log(arr);
// 方法1 Array.prototype.slice.call()
const arr0 = Array.prototype.slice.call(arr)
console.log(Object.prototype.toString.call(arr0));
console.log(arr0);
// 方法2 [].slice.call()
const arr1 = [].slice.call(arr)
console.log(Object.prototype.toString.call(arr1));
console.log(arr1);
// 方法3 ES6中的 Array.from()
const arr2 = Array.from(arr)
console.log(Object.prototype.toString.call(arr2));
console.log(arr2);
// 方法4 ES6中的 扩展运算符
const arr3 = [...arr]
console.log(Object.prototype.toString.call(arr3));
console.log(arr3);
</script>
打印结果:
什么是闭包、有什么作用?
闭包(closure)指有权访问另一个函数作用域中的变量的函数。
闭包的主要作用:延伸了变量的作用范围
深拷贝和浅拷贝:
浅拷贝:只拷贝最外的一层,里层如果还嵌套了对象,则只是拷贝了对象的地址(没有另外开辟空间),所以拷贝完后的数据地址是共享的任意一个对象修改了里层对象的属性值,二者的值都会变化
ES6 浅拷贝语法糖:Object.assign(o, obj); // 将obj浅拷贝给o对象
深拷贝:拷贝多层,每一级别的数据都会拷贝,复杂数据类型也是拷贝了地址的,数据的存储地址没有共享,所以拷贝完成后两个对象的数据是没有地址的关系的,任一修改了属性值另外一个不会受影响(要实现深拷贝可以借助递归函数的思路封装函数进行拷贝)
this 指向问题,普通函数和箭头函数的 this,以及怎么改变普通函数里的 this 指向
在普通函数中的 this 总是代表它的直接调用者,在默认情况下,this 指向的是 window ,在严格模式下,没有直接调用者的函数中的 this 为 undefined 。
箭头函数没有自己的 this , 它的 this 是继承而来,默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象,定义它的时候,可能环境是 window ,也有可能是其他的。
- 改变函数内 this 指向的三种方法:
1.call(obj, a, b); 此方法可以用于调用函数,表示将 this 改变为指向 obj 对象,a,b 是参数非必须
2.apply(obj, [ ]); 此方法可以调用函数和改变 this 指向,但是此方法的第二个参数必须是数组(伪数组)。apply() 的主要应用:我们可以利用它借助与数组内置对象求数字型数组的最值:
var arr = [11, 32, 4, 52, 33, 5, 53, 72, 45, 34]
console.log(Math.max.apply(Math, arr)) // 72
console.log(Math.min.apply(Math, arr)) // 4
3.bind(obj, a, b) 方法可以在不调用函数的情况下改变函数的 this 指向,方法返回的是原函数改变this 之后产生的新函数。如果有的函数我们不需要立即调动,但是又需要改变这个函数内部的 this 指向,此时就需要使用 bind() 方法,比如改变定时器内部的 this 指向(定时器中函数内的 this 默认指向全局 window 对象)。
- call(), apply(), bind() 总结:
三者都可以改变函数内部的this指向;
call() 和 apply() 改变 this 指向时都会调用函数,但 call() 传递的参数是 a,b,c 的形式,apply() 的参数则必须是数组形式;bind() 方法可以在不调用函数的情况下改变函数内部的 this 指向。
ES6 部分
ES6的新特性有哪些?
- let、count 关键字:(var、let、const 三者的异同比较:)
var | let | const |
全局作用域 | 块级作用域 | 块级作用域 |
存在变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 声明并赋值,值不可更改 |
- 数组、对象的解构赋值:
数组的解构赋值:即允许我们按照一一对应的关系从数组中提取值然后将值赋值给变量,如果数组中的值不够对应,超出的取到的是 undefined 。示例如下:
let arr = [1, 2, 3];
let [a, b, c, d, e] = arr; // 1 2 3 undefined undefined
对象的解构赋值:即允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量。示例如下:
let obj = { name: 'zhangsan', age: 18, sex: '男' };
let {name, age, sex} = obj;
console.log(name+age+sex); // zhangsan18男
- 箭头函数:() => { } 箭头函数有如下特点:
1.在箭头函数中,如果函数体中只有一句代码并且代码的执行结果就是函数的返回值,则函数体的大括号可以省略不写;
2.在箭头函数 中,如果形参只有一个,则形参外的小括号可以省略不写;
3.箭头函数中的 this :箭头函数不绑定 this ,箭头函数没有自己的 this 关键字,如果在箭头函数中使用 this,它将指向箭头函数定义的位置中的 this(即这个 this 指向箭头函数上一层的 this )
- 剩余参数:见下方案例:
// 注:剩余参数和解构赋值配合使用:
let arr = ['张三', '李四', '王五'];
let [s1, ...s2] = arr;
console.log(s1); // 张三
console.log(s2); // (2) ["李四", "王五"] s2 就获得了 arr 的剩余参数
- 扩展运算符
扩展运算符可以用于合并数组 arr3 = [...arr1, ...arr2] 、arr2.push(...arr1) ,还可以利用扩展运算符将伪数组转换为真正的数组。
- Array.from() 和 .find() 方法
Array.from() 方法可以用于将一个伪数组转化为标准数组,第二个参数即回调函数可以实现操作该伪数组的每一项;find() 方法用于查找数组中第一个符合要求的元素,在回调箭头函数中书写查找的要求即可,返回的是查找到的元素。
- 模板字符串、模板字符串的功能和特点
1.模板字符串中可以使用 ${} 调用变量/表达式/函数等
2.模板字符串中可以换行(打印出来的结果也有换行效果)
3.模板字符串中可以识别元素标签。如div/span/p/li等
- startsWith() 和 endswith() 方法:
startsWith() 方法用于判断字符串是否以某个字符开头,返回布尔值
endsWith() 方法用于判断字符串是否以某个字符结尾,返回布尔值
- Set 数据结构
Set 和 Array、Object 等一样都是一个数据结构,Set 中存储的数据不会重复。
promise 和 async/await 有什么区别?await 有什么特点?这两个方法报错了怎么抓取?
Promise 的出现主要是为了解决 Ajax 异步编程出现的类似于回调地狱这样的问题,它是异步编程的一种解决方案,将回调嵌套式的编程变成了链式编程结构。它通过 then 方法来定义和执行异步程序执行成功后的逻辑,用 catch 方法来捕获异常,而且可以链式调用。
new Promise(function (resolve, reject) {
resolve('执行成功')
reject('执行失败')
})
.then((result) => {
// result 是执行成功返回的结果
})
.catch((error) => {
// error 是执行失败返回的结果
})
async 函数是使用 async关键字声明的函数。 async 函数是 AsyncFunction 构造函数的实例, async 一般搭配 await 关键字使用。async/await 可以让我们
像编写同步代码一样编写异步代码,通过 try-catch
可以同步地捕获异常。一个 async 函数中可能包含0个或者多个 await 关键字。await 修饰的表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作执行完成后才会恢复进程。promise 的解决值会被当作该 await 表达式的返回值。
注:async 函数一定会返回一个promise 对象。async/await 是异步编程的最终方案。
async test() {
const result = await new Promise(function (resolve) {
// 这里执行异步任务
setTimeout(() => {
resolve(100)
}, 2000) // 延迟 2 秒
})
alert(result)
},
- await 的特点:
1.await 只能在 async 函数内部使用,必须在 await 的父级函数名前面标记一个 async 关键字;
2. async 函数会从上往下执行且遇到 await 表达式会等待该表达式执行完成后才会继续向下执行;
3.async 函数会自动将一个函数转化成 Promise ,返回值也是一个 Promise 对象;
4.await 放置在 Promise 调用之前,且会强制后面的代码等待,直到 Promise 对象 resolve。该值即为 await 表达式的返回值。
使用 async 和 await 来处理异步任务时,可以通过 try/catch 来捕获 Promise 的异常。示例如下:
async getCatch() {
try {
await new Promise(function (resolve, reject) {
reject('出现了异常问题')
})
} catch (error) {
console.log(error) // 出现了异常问题
}
},
Vue 部分
Vue 的生命周期
beforeCreate 在实例初始化之后,数据观测(data observer)之前被调用
created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch/event 事件回调。但这里还无法获取到页面元素
beforMount 在挂载开始之前被调用:相关的 render 函数首次被调用
mounted el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子,这里可以操作DOM了
beforeUpdate 数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前(即页面重新渲染之前)
updated 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子(即页面重新渲染完成之后)
beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用
destroyed Vue实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不会被调用。
每个生命周期内部可以做什么事
created 实例已经创建完成,因为它是最早触发的原因所以可以进行一些数据、资源的请求。
mounted 实例已经挂载完成,可以进行一些DOM操作
beforeUpdate 可以在这个钩子中进一步地更改状态,这不会触发页面的重新渲染
updated 可以执行依赖于DOM的操作。然而在大多数情况下应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestory 可以解绑事件、清除定时器等
destroyed 可以执行一些优化操作,清空定时器,解除绑定事件,释放内存等。
mounted 和 created 的区别?
在 created 中DOM节点还没有挂载所以无法在此进行DOM操作,而在 mounted 中 Vue 实例已经挂载完成所以可以进行DOM操作
Vue中 v-if 和 v-show 的区别?
v-if 如果条件不成立则不会渲染当前指令所在节点的DOM元素
而 v-show 只是控制当前DOM元素的样式进行显示与隐藏在实际的网页文档中该元素节点是存在的。(v-show 性能更好)
同一个标签上为什么 v-for 和 v-if 不能连用?
因为 v-for 会比 v-if 的优先级高一些,如果连用的话会把 v-if 给每个 v-for 循环生成的元素都添加一次,会造成性能问题。(建议使用时在 v-for 标签外包裹一层,在包裹的元素上使用 v-if)
简述 Vue 中 diff 算法原理
diff 算法即根据一个DOM元素生成的 虚拟DOM,然后DOM元素发生变化后修改虚拟DOM的算法原理:(其实就是比较两棵算法树)
①先同级比较,再比较子节点;
②先判断一方有子节点一方没有子节点的情况:新的有旧的无则将新的插入到旧的中即可,新的无旧的有则删除旧的内部多余的即可;
③比较都有子节点的情况
④递归比较子节点
为什么用 v-for 时为 key 指定一个唯一的值可以提高性能?
因为 Vue 底层会对虚拟DOM进行强比对,如果没有 key 则通过循环创建出来的DOM元素都是一样的,所以操作DOM时 Vue 底层实际并不知道我们实际操作的是哪个对象,它只会进行元素比对,比对完毕后进行相应的操作,比如增或者删,而为 key 指定了唯一的值后,Vue 底层通过比对前后两个DOM之间的差异很容易就可以确定是哪个元素发生了变化、哪些元素没有任何变化,从而只去相应更新有变化的元素。
组件中的 data 为什么是一个函数?
同一个组件被复用多次,会创建多个实例。这些实例用的是同一个构造函数,如果 data 是一个对象那么所有组件都共享了同一个对象,为了保证组件的数据独立性要求每个组件必须通过 data 函数返回一个对象作为组件的状态。
v-model 中的实现原理及如何自定义 v-model ?
v-model 可以看成是 value + input 方法的语法糖或者叫组合体。
组件传值:父传子、子传父、兄弟之间传值?
- 父组件向子组件传值
1.在子组件的 props 中以数组形式接收父组件传递过来的值;
2.父组件在使用组件时在组件属性中使用 v-bind (语法糖为 :)指令动态传值给子组件。
- 子组件向父组件传值
1.在子组件中通过 $emit() 来触发父组件中的自定义事件并传值,参数1为自定义事件名,参数2为要传递给父组件的值。
2.父组件在使用子组件时通过 v-on (语法糖为 @)来定义自定义事件并监听该事件的响应,通过 $event 作为事件响应参数来接收子组件传递过来的值。
- 非父子组件间传值(如兄弟组件)
1.非父子组件间的互相传值相当于发布订阅模式;
2.通过 $emit() 去触发别的组件的自定义事件,并将组件中的值作为第二个参数传递出去;
3.通过 $on() 来监听自定义事件的触发并通过第二个参数来接收组件传递的值。
Vue 双向绑定的原理?
Vue 的双向绑定原理就是 MVVM
核心:Object.defineProperty()
默认 vue 在初始化数据时,会给 data 中的属性使用 Object.defineProperty 重新定义所有属性,当页面取到对应属性时。会进行依赖收集(收集当前组件的 watcher)如果属性发生变化会通知相关依赖进行更新操作。
Vue 中 Computed 的特点?
理解:computed、watch、method 之间的区别
默认 computed 是用于定义计算属性的,也是一个 watcher 是具备缓存的,内部做了一个 dirty 实现了缓存的机制,取值的时候只有数据变了才会去执行计算属性的方法,否则就直接使用缓存。所以能提升性能。
注:此篇“前端面试”汇总是博主根据个人经历搜集得来题型并不完善也不具备权威性,答案也是博主自行整理,如有纰漏出错之处还望海涵并指出,博主也会不定时查漏补缺、完善和修正。未完待续...