回顾面试题
this指向问题
题1
题2
题3
题4
css面试题
- BFC(块级格式化上下文,用于清除浮动,防止margin重叠) 独立的容器
- BFC的生成
1 | float 不为none的元素 |
- 常见的块元素有
<h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>
等,其中<div>
标签是最典型的块元素。 - 常见的行内元素有
<a>、<strong>、<b>、<em>、<i>、<del>、<s>、<ins>、<u>、<span>
等,其中<span>
标签最典型的行内元素。有的地方也成内联元素 - 在行内元素中有几个特殊的标签——
<img />、<input />、<td>
,可以对它们设置宽高和对齐属性,有些资料可能会称它们为行内块元素。 - H5语义化元素:header,nav,main,article,section,aside,footer,small,strong
基础面试题
1 | // 给定后转换成指定的输出形式 |
- 给定一个正偶数,算出他有几种方式拆分成两个素数相加的形式
1 | let a = 10 |
面试题问答
微信小程序是如何实现登录的
- 首先需要一个按钮来触发事件
- 调用微信小程序的登录接口
wx.login
,拿到code - 调用微信小程序的获取用户的接口
wx.getUserProfile
拿到个人信息 - 拿到个人信息后调用后台的接口,将个人信息在传入到后台,登录后将相关的信息缓冲在本地存储中,方便后续的开发使用
闭包的概念,作用,应用及缺点
- 概念:函数嵌套函数,内部函数就是闭包;能够读取其他函数内部变量的函数
- 作用:可以读取函数内部的变量;可以使变量的值始终在内存中,不会被垃圾回收机制回收;可以避免使用全局变量,防止全局变量污染
- 应用:for循环中的保留i的操作;防抖和节流;函数柯里化
- 缺点:容易引起内存泄漏
原型和原型链
- 原型:每个函数上都有prototype属性,该属性指向原型对象;使用原型对象的好处就是所有的实例共享它所包含的属性和方法
- 原型链:每个对象上都有一个原型对象,通过__proto__指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,最终指向null
继承
总结
- 实现继承共有6种方法,有:原型链继承,构造函数继承,组合式继承(原型链+构造函数),原型继承,寄生式继承,寄生组合式继承
- 这几种方法 每种方法都有各自的特点
- 原型链继承他会将子函数原型指向要继承的函数实例,这样会造成共用一个原型对象,当一个实例发生变化,另一个也随之跟着变化
- 构造函数继承 只能继承父类的实例的属性和方法,不能继承父类原型的属性和方法
- 组合式继承 原型链和构造函数继承结合 会导致 在执行两次构造函数,影响性能;并且这里的constructor可进行遍历(应该禁用)
- 原型式继承 使用es5中的方法Object.create方法 创建一个空的对象,将新对象的原型指向对象
- 寄生式继承 在原型式继承的基础上,在父类的方法上添加了一些方法
- 寄生组合式继承 使用工厂函数进行封装处理 使用es5中的Object.defineProperty方法对函数原型(实例原型)的constructor的值进行配置enumerable为false
原型链继承
1 | function Parent1() { |
- 缺点:实现数据共享,当其中一个实例发生改变 另一个也随之跟着改变
构造函数方法(call方法)
- 缺点:只能继承父类的实例的属性和方法,但是不能继承父类原型的属性和方法
1 | function Parent1(){ |
组合继承(前两种方法)
- 缺点:多执行了一次构造函数,并且这里的constructor可进行遍历
1 | function Parent3 () { |
原型式继承
- 使用Object.create方法来实现,多个实例的引用类型属性指向相同的内存,存在篡改的可能 与原型链继承类似
1 | let parent4 = { |
寄生式继承
1 | let parent5 = { |
寄生组合式继承
1 | function clone (parent, child) { |
节流和防抖
- 防抖就是在某一时间内快速执行事件,然后仅最后一次事件执行
1 | const dedounce = function (fn, delay) { |
- 节流控制执行次数,在某段时间内如果执行多次,将仅仅执行一次
1 | const throttle = function(fn,delay){ |
apply,call,bind方法
- 都是用来改变this指向问题的
- apply,call是立即执行,bind返回一个函数
手写实现一个apply
1 | Function.prototype._apply = function (context, arg) { |
手写实现一个call
1 | Function.prototype._call = function (context, ...arg) { |
手写实现一个bind
1 | Function.prototype._bind = function (...args) { |
ajax实现过程
- 创建一个XMLHttpRequest对象
- 在创建好的对象下使用
open
方法,创建一个http请求 - 并且设置请求头的信息,比如
content-type
- 设置响应http请求状态变化的函数
onreadstatechange
- 发送http请求,使用send方法
- 获取异步调用时返回的结果
- 状态码 0-4 分别表示 未初始化状态,初始化状态,发送请求时的状态,接受请求时的状态,完成时的状态
axios实现原理
- 它是一个基于Promise的http库,可以在浏览器和node.js中使用
- 从浏览器中创建XMLhttpRequest 从node.js创建一个http请求
- 支持promise API
- 可以转换请求数据和响应数据,并对响应的内容自动转换为json类型的数据
- 取消请求,request.abort
- 客户端支持防御XSRF(跨站请求伪造)
浅拷贝和深拷贝
- 深拷贝是指:开辟了一个新的空间,然后将对象指向该地址,并且与原来的对象互不干扰
- 浅拷贝是指:比较浅的拷贝,他与原来的变量还是指向同一个地址,两者之间相互影响
- 原始类型:String,Null,undefined,boolean,number,symbol
- 引用类型:Object,function,Array
实现浅拷贝的方法:
- 针对引用数据类型 使用for in的方法实现
实现深拷贝的方法
- 使用
for..in..
+递归
的方式实现 - 使用
JSON.parse(JSON.stringify(对象))
实现深拷贝
箭头函数和普通函数的区别
- 外形也不同箭头函数使用箭头定义
- 箭头函数全是匿名函数;普通函数可以有匿名函数也可以有具名函数
- 箭头函数没有this,它的this指向上层作用域;普通函数的this指向调用它的对象,如果用作构造函数,则指向它的实例
- 箭头函数没有arguments;普通函数每一个都具有arguments,用来存储实际传递的参数;
- 箭头函数没有prototype属性
- 箭头函数不能使用new 来构造函数
跨域的解决方法
- 使用
jsonp
解决,缺陷只能进行get请求,实现原理就是创建一个script元素,将请求的参数赋值给src然后配合后端,数据通过函数执行传参的方式实现数据传递 - 使用
document.domain + iframe
实现 - 使用
window.name + iframe
来实现 - 使用nginx 配置 “
access-control-allow-origin
“ 设置跨域资源共享 - 使用
location.hash + iframe
来实现 - 使用
webSockets
来实现 - 使用
window.postMessage
来实现
什么是柯里化,手写实现
- 把多参数的函数,转换为单参数的函数(参数复用,闭包原理)
1 | function currying() { |
什么是事件监听、事件委托
- 事件委托:将子元素要执行的响应事件,绑定到父元素上,让父元素去代替监听动作;原理是事件冒泡
- 事件捕获和事件冒泡的执行顺序:先捕获后冒泡
- 事件监听 使用
addEventListener
实现
什么是Event LOOP(事件循环)
- 事件循环是javascript的执行机制(先同步后异步,异步先微任务后宏任务)
- 常见的宏任务:整段代码script,setTimeout,setInterval
- 常见的微任务:promise,process.nextTick()
垂直局中的几种实现方法
- 使用flex方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<style>
.name {
width: 100px;
height: 100px;
border: 1px solid #a71111;
}
body{
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<div class="name"></div>
- 定位实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<style>
.name {
width: 100px;
height: 100px;
border: 1px solid #a71111;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
body{
margin: 0;
height: 100vh;
}
</style>
<div class="name"></div>
<script> - 定位实现+transform
1 | <style> |
- 使用定位+margin方法实现
1 | .name{ |
使用text-align+line-height
使用grid方法
1 | body { |
- 定位+calc方法
get,post的区别
- get一般用来向服务器获取数据,而post则是向服务器发送数据
- get请求的时候是将请求的信息都暴露在url上,而post是将数据都放到body中
- get地址可以被缓冲,而post不能
- get请求长度是有限制的,而post请求的长度是没有限制的
localstorage,sessionStorage,cookie的区别
- 它们都是存储到客户端
- cookie数据始终在同源的http请求中携带(即使不需要), 即cookie在浏览器和服务器间来回传递, 而sessionStorage和localStorage不会自动把数据发送给服务器, 仅在本地保存.cookie数据还有路径(path)的概念, 可以限制cookie只属于某个路径下
- 存储大小限制也不同, cookie数据不能超过4K, 同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据, 如会话标识.sessionStorage和localStorage虽然也有存储大小的限制, 但比cookie大得多, 可以达到5M或更大
- 数据有效期不同, sessionStorage: 仅在当前浏览器窗口关闭之前有效; localStorage: 始终有效, 窗口或浏览器关闭也一直保存, 因此用作持久数据; cookie: 只在设置的cookie过期时间之前有效, 即使窗口关闭或浏览器关闭
- 作用域不同, sessionStorage不在不同的浏览器窗口中共享, 即使是同一个页面; localstorage在所有同源窗口中都是共享的; cookie也是在所有同源窗口中都是共享的
UDP和TCP的区别
- tcp是面向连接的(需要进行三次握手和四次挥手),udp是面向无连接
- tcp是面向字节流的,udp是面向数据报的
- tcp的请求是有顺序的
- tcp相较udp是比较可靠的,不会出现丢包的情况
- udp具有较好的实时性,比如直播,实时转播画面
相对定位和绝对定位
- 相对定位(relative):相对定位是相对于元素在文档中的初始位置,元素会占据原来的位置
- 绝对定位(absolute):绝对定位是相对于元素最近的已定位的祖先元素
ES6中新特性(数组方法)
- let,const 块级作用域
- 模板字符串
- 解构赋值
- 箭头函数
- 扩展运算符
- Promise
- 新增对象方法,字符串方法,数组方法
- 对象新增:Object.assign,Object.is方法
- 字符串新增:includes,repeat,padStart,padEnd
- 数组新增:of,from,find,findIndex,copyWithin,fill,includes,flat
数组方法改变原数组和不改变原数组
- 改变原数组:
- splice:有删除插入替换功能,第二个参数为 要改变的数组长度
- pop:删除数组尾部的一个元素,返回删除的元素值
- shift: 删除头部的一个元素,返回删除的元素值
- copyWithin:不会改变数组的长度但是会改变数组本身的内容,参数(目标位置,start,end)
- fill: 会改变原数组,参数(value,start,end)
- push: 会改变原数组,返回数组的长度
- 不会改变原数组的方法
- concat:方法将两个数组进行合并处理,返回一个新的数组
- filter: 返回一个新的数组,数组长度也可能发送变化
- map:返回一个新的数组,数组的长度不变
- find: 返回符合条件的第一个元素的值
- findIndex: 返回符合条件的第一个元素的索引值
- slice: 用于截取数组返回一个新的数组
Promise的概念、作用、以及实际运用
手写一个Object.create
1 | function fn(obj){ |
1.创建一个临时性的构造函数
2.将传入的对象作为这个构造函数的原型
3.最后返回了这个临时类型的一个新实例。
判断类型的方法
- typeof
- 基本数据类型可以使用
typeof
的方法去判断 typeof null
为:Object
typeof NAN
为number
- 对于创建的对象一般返回
Object
- 使用instanceof对已知的对象类型进行判断
- 使用object原型方法toString,返回类似于 [Object Object] 形式的值,使用切割的方法将类型分割出来
前端模块化
CommonJs(典型代表:node.js早期):
它通过 require 来引入模块,通过 module.exports 定义模块的输出接口
这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的
因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题
但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适AMD(典型代表:require.js):
这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行
所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范CMD(典型代表:sea.js):
这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范
它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同ES6 Module:
ES6 提出的方案,使用 import 和 export 的形式来导入导出模块
常见的状态码
301永久重定向,302 临时重定向,304 资源已经找到常用于协商缓存
400 请求报文内有语法错误,401 该状态码表示发送的请求需要通过HTTP认证,403 请求资源的访问被服务器拒绝,404 服务器上找不到请求资源 或路径错误
500 服务器端在执行请求时出错,502 代理服务器或网关从上游服务器中收到无效响应,503 服务器暂时处于超负载或停机维护,目前无法处理请求
vue常见的面试题
vue-router说说你了解的
- 两种模式hash模式和history模式
- 两种的区别:
- 都是依靠浏览器自身的特性;
- hash模式是在浏览器地址中加一个#号,虽然改变了url地址,但是它不会包括在http请求,只是用来指导浏览器的动作
- history模式则是没有#号 但是在页面刷新的时候如果后端没有进行配置,并且访问不到资源就会造成404局面,因此需要去配置好nginx进行拦截处理
vue2和vue3的区别
vue2和vue3的数据双向绑定的原理发生了变化
- vue2使用的是es5中的API Object.defineProperty()对数据进行劫持结合发布者订阅者模式的方式来实现;
- vue3使用的是ES6中的API proxy 进行数据代理
vue3支持碎片化节点,组件中可以有多个根节点
vue2和vue3使用的API不同
- vue2 使用的是Options API选项形式;
- vue3 使用的是合成型API composition API 组合式API;
vue2和vue3它们的生命周期不同
- vue2:
- beforeCreate
- created
- beforeMunted
- Mounted
- beforeUpdate
- Update
- beforeDestory
- destoryed
- activated
- deactivated
- vue3:
- setup()
- onBeforeMount
- onMouted
- computed
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmouted
- onActivated
- onDeactivated
- vue2:
它们建立数据方式不同
- vue2: 直接在data中创建
- vue3: 在setup中创建,该组件在组件初始化构建时候触发
父子的传参方式不同
vue3新增了瞬移组件 teleport
Vuex面试题
nextTick vue
用途:
在视图更新之后,基于新的视图进行操作,使用nextTick中的回调函数,在下一次更新DOM更新循环结束之后执行回调为什么使用$nextTick?
DOM更新是异步操作,在数据更新完成之后,DOM不会立即更新,而是等同一事件循环中的所有数据变化完成之后,在统一进行视图更新。所以使用$nextTick可以使同步任务在DOM更新完成之后去执行。
例如:
获取设置输入框由隐藏变为显示,再去获取这个DOM元素就需要使用$nextTick
在 created 和 mounted 阶段,如果需要操作渲染后的试图,也要使用 nextTick 方法。
注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted原理:事件循环
vue eventBus
EventBus 是消息传递的一种方式,基于一个消息中心,订阅和发布消息的模式,称为发布订阅者模式。
on 订阅消息 emit 发布消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20lass Bus {
constructor () {
this.callbacks = {}
}
$on(name,fn) {
this.callbacks[name] = []
this.callbacks[name].push(fn)
}
$emit(name,args) {
if(this.callbacks[name]){
//存在遍历所有callback
this.callbacks[name].forEach(cb => cb(args))
}
}
}
const EventBus = new Bus()
EventBus.$on('fn1', function(msg) {
alert(`订阅的消息是:${msg}`);
});
EventBus.$emit('fn1', '你好,世界!');vue父子组件传值单例问题
数据从父组件传递给子组件,子组件内部不能修改从父级传过来的数据。防止子组件意外改变父组件的状态
EventBus 说说你了解的
如果使用不善,EventBus会是一种灾难,到底是什么样的“灾难”了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。还要就是如果业务有反复操作的页面,EventBus在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理EventBus在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus事件监听。
有三种方法
$emit 用于发送数据
$on 用于接受监听数据
$off 用于销毁监听事件
computed和watch的区别
- computed是一对多,多次调用,把第一次调用的结果放入缓冲,节约性能
- 在computed中定义一个函数,看起来是一个函数,其实是一个属性
vue2和vue3的数据双向绑定
- 为什么要做这个改进? vue2中使用的是ES5 objec.defineProperty 方法
vue2- 关于对象:对页面初始化实例中的propery进行getter和setter的转化,所以必须property必须在data对象上存在才能让vue将他转变为响应式的
- 关于数组: 当你使用索引值去直接改变数组时,或者当你直接去修改数组的长度时,不能检测数组的变动
vue3 - 使用了ES6语法Proxy方法 用于定义基本操作的自定义行为,通过Reflect(反射): 对源对象的属性进行操作.
vue3 ref和reactive
ref
用来定义响应式数据的(可以是基本类型也可以是引用类型)reactive
定义一个对象类型的响应式数据,基本数据类型还是使用refref
通过Object.defineProperty 中的get和set实现数据劫持(响应式)reactive
通过proxy 数据代理的方式去实现数据的响应式,并且使用reflect 来操作源对象内部的数据
v-if和v-show的区别
- 条件隐藏 使用display:none实现
- v-if 条件渲染,销毁和创建元素
key值的作用
- 可以给每一个节点有唯一的标识,当添加或删除节点时;通过对比数据前后的变化,只用于操作某个变化的节点
- 不需要重新渲染所有数据,提高了性能
执行顺序 生命周期
1、父子组件的加载顺序为:
父beforeCreated ->父created ->父beforeMounted ->子beforeCreated ->子created ->子beforeMounted ->子mounted -> 父mounted
2、父组件更新顺序为:
父beforeUpdate->父updated
3、子组件更新顺序为:
父beforeUpdate->子beforeUpdate->子updated->父updated
4、父子组件销毁顺序为:
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed