前言
查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?因为本文有一万多字,有点长,所以建议你可以先看看我的面经【查】到自己哪里【漏】了,再回来【补缺】(腾讯、百度、小米、网易等实习面经)
本文是对自己的前段时间面试的一篇题目及解答总结。其实本来自己是不太想继续写关于面试题的文章了,因为社区内很多这类的文章,但是如果每个地方翻一下,又不方便自己回顾,所以还是决定写下本文供自己巩固
HTML5
1.新特性
-
语义标签
-
增强型表单
- 新增表单属性:
placehodler
,autofocus
,multiple
,required
,minlength
,maxlength
,max
,min
,pattern
- 新增表单属性:
-
视频和音频
HTML5
提供了播放音频文件的标准,即使用<audio>
,<video>
元素
-
Canvas
绘图- 标签只是图形容器,必须使用脚本来绘制图形
-
SVG绘图
-
HTML5 Geolocation
(地理定位)用于定位用户的位置 -
拖放API
ondragstart
: 元素开始被拖动时触发 作用在拖拽元素上ondragenter
:当拖曳元素进入目标元素的时候触发的事件,作用在目标元素上ondragover
:拖拽元素在目标元素上移动的时候触发的事件,作用在目标元素上ondragleave
:拖拽元素拖离开了目标元素时触发,作用在目标元素上ondrop
:被拖拽的元素在目标元素上同时鼠标放开触发的事件,作用在目标元素上ondragend
:当拖拽完成后触发的事件,作用在被拖曳元素上
-
WebWorker
- HTML5的规范中提供了一个多线程的解决方案,这就是
WebWorker
(WebWorker学习参考文章)
- HTML5的规范中提供了一个多线程的解决方案,这就是
-
WebStorage(这个老生常谈了,就不展开了)
-
WebSocket
- 是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议(WebSocket教程参考文章)
CSS
1.CSS选择器
CSS 几种常见选择器优先级是:!important
> 内联 > ID选择器 > 类选择器 > 标签选择器 > 通用选择器
- ID选择器
- 类选择器
- 标签选择器
- 后代元素选择器
- 作用:选中指定元素的指定后代元素。语法:祖先元素 后代元素 {}(空格隔开)
- 子元素选择器
- 作用:选择指定父元素的指定子元素。语法:父元素 > 子元素
- 伪类
- 伪类 表示元素的一种特殊状态
:active
向被激活的元素添加样式:focus
向拥有键盘输入焦点的元素添加样式:hover
当鼠标悬浮在元素上方时,向元素添加样式:link
向未被访问的链接添加样式:visited
向已被访问的链接添加样式。(隐私问题只能设置颜色):first-child
向元素的第一个子元素添加样式:lang
向带有指定lang
属性的元素添加样式::selection
匹配被用户选中或处于高亮状态的部分
- 伪元素
- :伪元素 表示元素中一些特殊的位置
:first-letter
向文本的第一个字母添加特殊样式。:first-line
向文本的首行添加特殊样式。:before
在元素之前添加内容。:after
在元素之后添加内容
- 其他子元素选择器
:first-child
指定父元素中第一个元素且为指定元素的样式
2.如何清除浮动
- 给浮动元素的父元素添加高度
- 在最后一个子元素新添加最后一个冗余元素,然后将其设置
clear:both
,这样就可以清除浮动 - 伪元素清除( 手写
clearfix
)
.clearfix:after {
content: '';
display: block;
clear: both;
}
- 给父元素使用
overflow:hidden
3.em、rem区别
em
可以说是相对于父级元素的字体大小,当父级元素字体大小改变时,又得重新计算rem
只相对于根目录,即HTML元素。有了rem
这个单位,我们只需要调整根元素html
的font-size
就能达到所有元素的动态适配了
4.重绘与回流(重排)
重绘不一定导致回流,回流一定会导致重绘
-
回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)
- 最“贵”的操作:改变 DOM 元素的几何属性
- “价格适中”的操作:改变 DOM 树的结构
- 最容易被忽略的操作:获取一些特定属性的值。当你要用到像这样的属性:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
、scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
、clientTop
、clientLeft
、clientWidth
、clientHeight
-
重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘
5.position粘性定位
sticky
粘性定位( 建议结合具体实践操作来进行理解,有的面试官会深挖 )
-
sticky
英文字面意思是粘,粘贴,所以可以把它称之为粘性定位 -
position: sticky;
基于用户的滚动位置来定位 -
粘性定位的元素是依赖于用户的滚动,在
position:relative
与position:fixed
定位之间切换 -
它的行为就像
position:relative;
而当页面滚动超出目标区域时,它的表现就像position:fixed;
,它会固定在目标位置 -
元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位
-
这个特定阈值指的是
top
,right
,bottom
或left
之一,换言之,指定top
,right
,bottom
或left
四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同
6.display:none与visibility:hidden
display:none
- 如果给一个元素设置了
display: none
,那么该元素以及它的所有后代元素都会隐藏,它是前端开发人员使用频率最高的一种隐藏方式。隐藏后的元素无法点击,无法使用屏幕阅读器等辅助设备访问,占据的空间消失
visibility: hidden
- 给元素设置
visibility: hidden
也可以隐藏这个元素,但是隐藏元素仍需占用与未隐藏时一样的空间,也就是说虽然元素不可见了,但是仍然会影响页面布局
7.style在body前与在body后的区别
- 写在body标签前利于浏览器逐步渲染
- 写在body标签后:由于浏览器以逐行方式对html文档进行解析,当解析到写在尾部的样式表(外联或写在style标签)会导致浏览器停止之前的渲染,等待加载且解析样式表完成之后重新渲染
8.Flex实现的原理
- 面试时候遇到的一个问题,我想的答案应该是flex底层原理,但至今还是没查到有关资料,(也有可能是我理解错意思了,难道面试官只是要我讲讲他的容器、属性之类的?)欢迎掘友们评论区指点
9.标准盒子模型于IE盒子模型区别
- 两者width不一样,标准盒子模型的 Width = content,IE盒子模型的Width = border + padding + content
10.z-index失效的场景
主要考察对层叠上下文的概念、以及层叠顺序、层叠等级的优先级问题(具体学习请参考彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index)
-
层叠上下文:层叠上下文(stacking context),是HTML中一个三维的概念。在CSS2.1规范中,每个盒模型的位置是三维的,分别是平面画布上的X轴,Y轴以及表示层叠的Z轴。一般情况下,元素在页面上沿X轴Y轴平铺,我们察觉不到它们在Z轴上的层叠关系。而一旦元素发生堆叠,这时就能发现某个元素可能覆盖了另一个元素或者被另一个元素覆盖
-
层叠等级:在同一个层叠上下文中,它描述定义的是该层叠上下文中的层叠上下文元素在Z轴上的上下顺序
-
如何产生“层叠上下文”
- HTML中的根元素本身j就具有层叠上下文,称为“根层叠上下文”
- 普通元素设置position属性为非static值并设置z-index属性为具体数值,产生层叠上下文
- CSS3中的新属性也可以产生层叠上下文
-
层叠顺序:按照如下图排列
11.CSSOM生成规则
构建CSSOM(CSS对象模型 - CSS Object Model)
- 构建CSSOM的过程与构建DOM的过程非常相似,当浏览器接收到一段CSS,浏览器首先要做的是识别出Token,然后构建节点并生成CSSOM(规则如下)
Bytes
→characters
→tokens
→nodes
→CSSOM
JS
1.var、let、const区别
1.1 var
var
声明的变量不存在块作用域var
声明的全局变量会自动变成window
对象的属性var
声明的变量会提升(声明会提前,但赋值不会,还是会报undefined
的错)var
声明的变量在同一个作用域下可以被重复声明,而let
和const
则不被允许,否则报错
1.2 let和const
let
命令,用来声明变量它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效const
声明一个只读的常量。一旦声明,常量的值就不能改变- 暂时性死区
- ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError报错 let tmp; }
2.赋值、深拷贝、浅拷贝区别
-
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
-
浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。
-
深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响
3.箭头函数
-
语法更加简洁、清晰 。从上面的箭头函数基本语法示例中可以看出,箭头函数的定义要比普通函数定义简洁、清晰得多,很快捷
-
箭头函数没有
prototype
(原型),所以箭头函数本身没有this
-
箭头函数没有自己的
this
,箭头函数的this
指向在定义(注意:是定义时,不是调用时)的时候继承自外层第一个普通函数的this
。所以,箭头函数中this
的指向在它被定义的时候就已经确定了,之后永远不会改变 -
箭头函数不绑定
arguments
,取而代之用rest
参数...
代替arguments
对象,来访问箭头函数的参数列表。箭头函数没有自己的arguments
对象。在箭头函数中访问arguments
实际上获得的是外层局部(函数)执行环境中的值
4.apply、call、bind作用及区别
4.1 共同点
- 三者都是用来改变函数的上下文,也就是改变
this
指向
4.2 不同点
bind
不会立即调用,而是返回一个绑定后的新函数。使用场景即:不需要立即指向的,而是像生成一个新的函数长期绑定某个函数给某个对象使用的时候
let newFn = fn.bind(this.Obj)
newFn(arg1,arg2,arg3...)
call
、apply
都是立即调用使用apply
是立即调用的,返回函数的执行结果,this指向第一个参数,第二个参数是一个数组,这个数组里的内容是fn函数的参数(fn.apply(this.Obj,[arg1,arg2,...])
)。使用场景:要传递的参数很多,则可以用数组将参数好调用call
也是立即调用的,返回函数的执行结果,this指向第一个参数,后面可有多个参数,并且这些都是fn函数的参数(fn.call(this.Obj,arg1,arg2...)
)。使用场景:要传递的参数不多,则可以使用
5.数组及字符的各种API方法
5.1 数组
-
创建数组
Array()
let arr = new Array()[]
let arr = []Array.of()
Array.of(1,2,3) // [1,2,3]
-
检测数组
Array.isArray(value)
-
转换方法:
toLocaleString()
toString()
-
let colors = ['red','yellow', 'blue']; console.log(colors.toString()) // red,blue,green console.log(colors.toLocaleString()) // red,blue,green
-
栈方法:
push()
在数组末尾添加元素(会修改原数组),返回修改后的数组的长度pop()
从数组的末尾移除最后一项(会修改原数组),减少数组的长度,返回删除的项
-
队列方法:
shift()
移除数组中的第一项(会修改原数组),并返回该项unshift()
在数组前端添加任意个元素(会修改原数组),并返回数组的长度
-
操作数组方法:
-
reverse()
反转数组(会修改原数组),并返回排序后的数组 -
sort()
默认情况下,按照升序排列数组项,调用每个数组项的toString()
方法,然后比较得到的字符串,确定如何排序(会修改原数组)。也可以接受一个函数。返排序后的数组 -
concat()
用于连接两个或多个数组arrayObject.concat(arrayX,arrayX,......,arrayX)
,会先创建当前数组的一个副本,然后将接受到的参数添加到这个副本的末尾,最后返回数新构建的数组(不会修改原数组) -
slice()
基于当前数组中的一个或多个项创建一个新数组,接受一或两个参数,既要返回的起始和结束位置(不会改变原数组) -
splice()
第一个参数起始位置,第二参数删除几个,第三个参数要插入的任意项。splice返回的是删除的项组成的数组,没有则返回空(可进行删除、插入、替换操作)(会修改原数组) -
fill()
该方法方法使用给定值,填充一个数组(会修改原数组)
['a', 'b', 'c'].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7]
还可以接收第二和第三个参数,用于指定填充的起始位置和结束位置
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
flat()
用于将嵌套的数组“拉平”,变成以为数组,返回一个新数组。接收一个参数,表示想要拉平的层数(不改变原数组)
-
-
查位置方法:
indexOf()
,lastIndexOf()
indexOf()
和lastIndexOf()
都接收两个参数:要查找的项和表示查找起点位置的索引。indexOf
方法从数组的头部开始查找,lastIndexOf
方法从数组的尾部开始查找。没找到则返回-1
findIndex()
返回第一个符合条件的数组成员位置,没找到返回-1,两个参数:每一项处理函数和(可选的)运行该函数的作用域对象
[1, 5, 10, 15].findIndex(function(value, index, arr) {//依次当前的值、当前的位置和原数组 return value > 9; }) // 2
-
查值方法:
find()
查找出第一个符合条件的数组成员,接收两个参数:一个遍历每一项的函数和(可选的)运行该函数的作用域对象
[1, 4, -5, 10].find((n) => n < 0) // -5
includes()
返回一个布尔值,接收两个参数:要查找的项和表示查找起点位置的索引
-
迭代方法:
-
every
对数组中的每一项运行给定函数,如果该函数对每一项都返回true
,则返回true
,否则返回false
(即全对即返回true
,否则都返回false
) -
some
与前者相对,该方法是只传入的函数对数组中的某一项返回true
,就会返回true
(即全错即返回false
,否则有一项符合都返回true
) -
filter
返回符合筛选条件的项所组成的一个新数组(不会改变原数组)
let number = [1,2,3,4,5]; let filterResult = number.filter((item, index, array)=>{ return item > 2 }) console.log(filterResult)// [3, 4, 5]
-
forEach
它只是对数组中的每一项运行传入的函数。没有返回值(遍历操作原数组,会修改原数组) -
map
返回一个新数组,而这个数组的每一项都是在原始数组中的对应项上运行传入函数的结果(不会改变原数组)
let number = [1,2,3,4,5]; let mapResult = number.map((item, index, array)=>{ return item * 2 }) console.log(mapResult)// [2, 4, 6, 8, 10]
-
6.遍历对象的方法
for in
主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性
var obj = {"name":"tom","sex":"male"}
Object.defineProperty(obj, "age", {value:"18", enumerable:false})
//增加不可枚举的属性age
Object.prototype.protoPer1 = function(){console.log("name is tom");}
//通过原型链增加属性,为一个函数
Object.prototype.protoPer2 = 2 //通过原型链增加属性,为一个整型值2
for(var a in obj){
console.log(a)
console.log(obj[a])
}
// 示例中的属性age为不可可枚举,所以没有输出
Object.keys
此方法返回一个数组,元素均为对象自有可枚举的属性
// (示例为上面代码)
console.log(Object.keys(obj))
// ["name","sex"]
Object.getOwnProperty
此方法用于返回对象的自有属性,包括可枚举和不可枚举的属性
// (示例为上面代码)
console.log(Object.getOwnPropertyNames(obj))
// ["name","sex","age"]
7.Es6常见新特性
- ES6类 Class
- for...of 和 for...in
- 对象的解构
- rest操作符 / Spread操作符
- 模板字符串
- const let
- promise
- async函数
- Module
8.Set、Map、WeakSet 和 WeakMap 的区别
详细区别请参考阮一峰老师的 ECMAScript 6 入门 Set 和 Map 数据结构
-
集合(Set)ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
-
映射(Map)是用于存取键值对的数据结构,一个键只能对应一个值且键不能重复
-
WeakSet结构和Set类似,都是不重复值的集合
- WeakSet的成员只能是对象,而不能是其他的值
- WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象。那么垃圾回收机制会自动会输该对象所占用的额内存,不考虑该对象还存在与WeakSet之中
-
WeakMap结构与Map结构类似,也是用于生成键值对的集合
- WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名
- WeakMap的键名所指向的对象,不计入垃圾回收机制
9.对原型、原型链的理解
像c语言有类实现继承、java可以通过抽象类实现继承,而js为了能将一类事物抽象出来,使相同一类事物能够拥有一样的属性跟方法,便将原型链作为实现继承的主要方式。原型链从数据结构来看,其实就是一个链表,实例有一个指向原型的指针,原型又包含一个指向构造函数的指针,层层递进
10.浏览器事件流的阶段以及事件监听的三个参数
DOM事件传播包括三个阶段:
- 1、捕获阶段
- 2、目标对象调用事件处理程序
- 3、冒泡阶段
第三个参数为true
就走捕获阶段,为false
就走冒泡阶段。捕获是不可被取消的,但冒泡可以。绑定事件默认是冒泡阶段
11.闭包使用问题
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除
12.如何清除闭包
闭包的含义是函数在调用时能够访问函数在定义时可以访问的作用域,例如在定义函数a的时候,a能够访问变量b。每一个函数都有自己对应的闭包,当函数没有被垃圾回收机制回收时函数对应的闭包也会常驻内存。如果需要清除闭包就要回收不需要的函数,根据JavaScript回收机制,当一个内存空间没有变量指向的时候就会被回收。那么闭包清除的方式就是将不需要的函数名赋值为null
框架
1.vue的data为什么是一个方法
-
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中
data
是一个对象,那么这样作用域没有隔离,子组件中的data
属性值会相互影响,如果组件中data
选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的data
属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题 -
一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果
data
是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data
不冲突,data
必须是一个函数
2.vue组件间通信问题
一般的父子组件、祖孙组件传值、Vuex应该都能答上来,但你能答得更清晰更全面吗?或许可以让面试官眼前一亮哦(请查看参考文章 Vue组件通信方式居然有这么多?你了解几种)
3.watch监听实现
简单阐述原理(具体了解请查看官方源码):vm
调用 $watch
后,首先调用 observe
函数 创建 Observer
实例观察数据,Observer
又创建 Dep
, Dep
用来维护订阅者。然后创建 Watcher
实例提供 update
函数。一旦数据变动,就层层执行回调函数
4.vue-router的hash跟history模式
4.1 hash模式
hash
模式, 原本用来结合锚点控制页面视窗的位置,具有以下特点:
-
在hash模式下,所有的页面跳转都是客户端进行操作,因此对于页面拦截更加灵活;但每次url的改变不属于一次http请求,所以不利于SEO优化
-
hash
模式是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发hashchange
事件。 -
可以改变URL,但不会触发页面重新加载(hash的改变会记录在
window.hisotry
中)因此并不算是一次http请求,所以这种模式不利于SEO优化 -
只能修改#后面的部分,因此只能跳转与当前URL同文档的URL
-
只能通过字符串改变URL
-
通过
window.onhashchange
监听hash的改变,借此实现无刷新跳转的功能
4.2 history模式
history
模式, 根据 Mozilla Develop Network 的介绍,调用 history.pushState()
相比于直接修改 hash,存在以下优势
-
history API
是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求 -
新的URL可以是与当前URL同源的任意 URL,也可以与当前URL一样,但是这样会把重复的一次操作记录到栈中
-
通过参数
stateObject
可以添加任意类型的数据到记录中 -
可额外设置
title
属性供后续使用 -
通过
pushState
、replaceState
实现无刷新跳转的功能 -
兼容性不如
hash
,且需要服务端支持,否则一刷新页面就404了 -
在
history
模式下,借助history.pushState
实现页面的无刷新跳转;这种方式URL的改变属于http请求,因此会重新请求服务器,这也使得我们必须在服务端配置好地址,否则服务端会返回404,为确保不出问题,最好在项目中配置404页面
5.vue3 Proxy跟vue2 defineProperty区别
5.1 defineProperty缺点
- 无法检测对象属性的添加或移除,为此我们需要使用
Vue.set
和Vue.delete
来保证响应系统的运行符合预期 - 无法监控到数组下标及数组长度的变化,当直接通过数组的下标给数组设置值或者改变数组长度时,不能实时响应
- 性能问题,当data中数据比较多且层级很深的时候,因为要遍历data中所有的数据并给其设置成响应式的,会导致性能下降
5.2 对比区别
Object.defineProperty
只能劫持对象的属性,对新增属性需要手动进行Observe
,而Proxy
是直接代理对象- 为什么
Proxy
可以解决以上的痛点呢? 本质的原因在于Proxy
是一个内置了拦截器的对象,所有的外部访问都得先经过这一层拦截。不管是先前就定义好的,还是新添加属性,访问时都会被拦截(proxy
具体学习请看阮一峰老师的ES6教程 Proxy)
6.computed和watch区别
6.1 概念
computed
: 是计算属性,依赖其它属性值,并且computed
的值有缓存,只有它依赖的属性值发生改变,下一次获取computed
的值时才会重新计算computed
的值watch
: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作
6.2 区别
- 相同:
computed
和watch
都起到监听/依赖一个数据,并进行处理的作用 - 不同:它们其实都是
vue
对监听器的实现,只不过computed
主要用于对同步数据的处理,watch
则主要用于观测某个值的变化去完成一段开销较大的复杂业务逻辑。能用computed
的时候优先用computed
,避免了多个数据影响其中某个数据时多次调用watch
的尴尬情况
7.在哪个生命周期内调用异步请求
可以在钩子函数 created
、beforeMount
、mounted
中进行调用,因为在这三个钩子函数中,data
已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created
钩子函数中调用异步请求,因为在 created
钩子函数中调用异步请求有以下优点
- 能更快获取到服务端数据,减少页面 loading 时间
- ssr 不支持
beforeMount
、mounted
钩子函数,所以放在created
中有助于一致性
8.虚拟DOM
8.1 虚拟DOM有什么好处?
- 假设一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的
diff
内容保存到本地一个JS对象中,最终将这个JS对象一次性attch
到DOM
树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在虚拟DOM上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制
8.2 通过DIFF算法对比操作JS对象实现差量更新
- 没有旧的节点,则创建新的节点,并插入父节点。
- 如果没有新的节点,则摧毁旧的节点。
- 如果节点发生了变化,则用
replaceChild
改变节点信息 - 如果节点没有变化,则对比该节点的子节点进行判断,使用递归调用
8.3 虚拟DOM实际渲染规则
Vue和React通用流程:vue template
/react jsx
-> render
函数 -> 生成VNode
-> 当有变化时,新老VNode diff
-> diff
算法对比,并真正去更新真实DOM
9.Vue3 新特性
Composition API
(看图,解决开发者在完成或阅读代码时上下反复跳转,同一个功能的代码不够聚合的问题)- 使用亮点:整个函数就是一个功能;函数包含创建新文件夹所依赖的数据和逻辑;函数完全独立,功能可以复用
- 告别
$set
- 即上面提到的
Proxy
重写了数据双向绑定的方法解决了痛点问题
- 即上面提到的
Fragments
- 简单来说就是,在vue3中组件官方支持声明多个根节点了。也就是说,下面这样的写法是可行的。同时需要我们显示的声明在何处分发,同时意味着render函数也可以返回数组了
attribute
- 简单来说就是,在vue3中组件官方支持声明多个根节点了。也就是说,下面这样的写法是可行的。同时需要我们显示的声明在何处分发,同时意味着render函数也可以返回数组了
Vue3.0
将对tsx
、class component
等有更好的支持destroyed
生命周期重命名为unmounted
beforeDestroy
生命周期重命名为beforeUnmount
10.vue底层重写数组的七个方法
10.1 概述
Vue监听Array三步曲(只是大体原理,具体请自己查看官方源码进行理解或者查看一些关于VUE源码的权威书籍)
- 第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化
- 第二步:对 Array 的原型方法使用
Object.defineProperty
做一些拦截操作 - 第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型
10.2 重写的数组方法
push()
,pop()
,shift()
,unshift()
,splice()
,sort()
,reverse()
11.vue、react区别
11.1 相同点
- 都是组件化开发思想
- 都支持服务器端渲染
- 都采用
Virtual DOM
,组件化开发,通过props
参数进行父子组件数据的传递,都实现webComponent
规范 - 数据驱动视图
- 都有支持
native
的方案,React的React native
,Vue的weex
- 都有管理状态,React有redux,Vue有自己的Vuex(自适应vue,量身定做)
11.2 不同点
- React严格上只针对MVC的view层,Vue则是MVVM模式
- virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树;而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要
shouldComponentUpdate
这个生命周期函数方法来进行控制 - 组件写法不一样,React推荐的做法是
JSX
+inline style
,也就是把HTML
和CSS
全都写进JavaScript
了,即all in js
;Vue推荐的做法是webpack
+vue-loade
的单文件组件格式,即html
,css
,js
写在同一个文件 - 数据绑定:vue实现了数据的双向绑定,react数据流动是单向的
state
对象在react应用中不可变的,需要使用setState
方法更新状态;在vue
中,state
对象不是必须的,数据由data
属性在vue对象中管理;
12.keep-alive
- keep-alive是Vue.js的一个内置组件。
<keep-alive>
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。 当组件在<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。它提供了include
与exclude
两个属性,允许组件有条件地进行缓存
13.vuex
13.1 vuex核心:
state
:存储store
的各种状态mutation
: 改变store
的状态只能通过mutation
方法action
: 异步操作方法module
: 模块化getter
: 相当于计算属性,过滤出来一些值
13.2 vuex使用
-
每一个 Vuex 应用的核心就是
store
(仓库)。“store”
基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同: -
Vuex 的状态存储是响应式的。当 Vue 组件从
store
中读取状态的时候,若store
中的状态发生变化,那么相应的组件也会相应地得到高效更新。 -
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit)
mutation
。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用
计算机网络
1.DNS解析
DNS (Domain Name System 的缩写)的作用非常简单,通俗来讲就是根据域名查出IP地址
1.1 DNS解析顺序
- 本地
hosts
文件 - 本地
DNS
缓存 DNS
服务器缓存DNS
服务器递归查找
1.2 DNS的记录类型
当我们在阿里云买了一个域名后,可以配置我们主机域名解析规则,也就是 记录 常见的 DNS 记录类型如下
A
:地址记录(Address
),返回域名指向的IP地址NS
:域名服务器记录(Name Server
),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址MX
:邮件记录(Mail eXchange
),返回接收电子邮件的服务器地址CNAME
:规范名称记录(Canonical Name
),返回另一个域名,即当前查询的域名是另一个域名的跳转PTR
:逆向查询记录(Pointer Record
),只用于从IP地址查询域名
1.3 DNS递归查找
DNS服务器是怎么递归查找的,(以这个地址为例tlab.cloud.tencent.com. 600 IN A 193.112.230.97
)
- 询问根域名,获取顶级域名
.com
的NS(Name Server)
和A(Address)
,NS
为顶级域名的名字,A
即NS
对应的ip地址 - 询问顶级域名,获取二级域名
.tencnet.com
的NS
和A
- 询问二级域名,获取三级域名
.cloud.tencent.com
的NS 和A
- 询问三级域名,获取四级域名
.tlab.cloud.tencent.com
的NS 和A
- 最后,将
tlab.cloud.tencent.com
的ip地址返回给用户,并且缓存 - 用户获取到真正的ip地址,并且缓存
2.Http1 和 Http2 区别
2.1 HTTP/1.1 缺点
-
队头阻塞(Head-of-line blocking)
- HTTP/1.1协议虽然可以在同一个TCP连接上发送多个请求,但是这多个请求是有顺序的,必须处理完第一个请求才会响应下一个请求。如果第一个请求处理的特别慢,后面的所有请求就需要排队。
-
TCP 连接数限制
- 对于同一个域名,浏览器最多只能同时创建 6 ~ 8 个TCP连接。如果一个页面有十个请求同时发送,那么只能等第一次的 6 ~ 8 个请求都返回了才能继续接下来的 2 ~ 4 个请求。这怎么能行?域名分片技术应运而生。就是把资源分配到不同的域名下(可以是二级子域名),这样就解决了限制,愉快~但是滥用域名分片技术也不行,因为每个TCP连接也是很费时的(这个大家都懂的)。
-
Header 内容繁多,有时有可能会超过响应内容,并且每次有许多字段都是重复传输。
-
HTTP/1.1是文本协议传输,不够安全
2.2 HTTP2相比于HTTP/1.1的新特性包括:
-
多路复用 (MultiPlexing),单一长连接,二进制格式传输,请求优先级设置
-
头部header压缩
-
服务端推送Server Push
3.Http 和 Https区别
HTTPS和HTTP的主要区别 (多了一层SSL or TSL)
-
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用
-
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
-
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
-
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
4.协商缓存跟强缓存区别
- 相同点:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据
- 不同点:强缓存不发请求到服务器,协商缓存会发请求到服务器
- 详情可以参考我之前的文章(前端一面基础知识 ⑥——性能优化、Web安全、Linux常用命令)
5.get、post区别
-
GET
请求参数会被完整保留在浏览器历史记录里,而POST
中的参数不会被保留 -
GET
参数通过URL传递,POST
放在Request body
中。GET
比POST
更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息 -
GET
用于获取信息,POST
用于修改服务器上的数据(一般情况下遵循这个规则) -
《图解HTTP》对这两者的说明的区别
GET
:用来请求访问已被URI识别的资源POST
:用来传输实体的主体(POST
与GET
功能很相似,但是POST
的主要目的不是获取相应的主体内容)
6.Cookie与Session的区别
-
cookie
数据存放在客户的浏览器(客户端)上,session
数据放在服务器上,但是服务端的session
的实现对客户端的cookie
有依赖关系的 -
cookie
不是很安全,别人可以分析存放在本地的COOKIE
并进行COOKIE
欺骗,考虑到安全应当使用session
-
session
会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE
-
单个
cookie
在客户端的限制是4K,就是说一个站点在客户端存放的COOKIE
不能超过4K
7.用axios给每个请求头前统一加一个字段
使用axios request 拦截器,axios.interceptors.request.use
总结
下一次再写面试相关文章可能要到春招了,也是我这名21届学生的DDL了。因为面试发现一些问题深挖就会答得很含糊,究其根本就是因为自己看的书不够多,很多概念没有一个认知,只知其表,不知其里(本文中很多解答也是一样的,如果你只看这些简要概念,那么你面试的时候,一旦面试官深挖,那么你就答不上来了,建议大家还是抽空去看看书,看看官方文档)。所以之后一段时间应该都是边实习边抽空看书,完善自己的整个知识体系,我之后可能也会写一些关于书籍中知识的文章。
写在文末
如果你觉得我写得还不错的话,可以给我点个赞哦^^,如果哪里写错了、写得不好的地方,也请大家评论指出,以供我纠正
其它文章
- 前端一面基础知识 ⑥——性能优化、Web安全、Linux常用命令
- 前端一面基础知识 ⑤——Http、Ajax、跨域
- 前端一面基础知识 ④——事件与DOM
- 前端一面基础知识 ③——异步(面试场景题)
- 前端一面基础知识 ②——作用域和闭包(面试场景题)
- 前端一面基础知识 ①——CSS面试题
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!