面试题all
1.为什么写key
Key会给每一个vnode增加一个唯一的id,可以依靠key更快更准确的找到oldvnode中对应的vnode节点,避免了就地复用的情况
2.Es5的继承和Es6除了写法以外还有什么区别
子类this的生成顺序不同,Es5的继承先生成了子类实例再调用父类的构造函数修饰子类。Es6则是先生成父类,再通过 super继承父类的实例方法
3.构造函数和class类的一个区别
1、class声明会提升但不会赋值,在class定义前new会报错,类似let const的暂时性死区
2、Class内部会采用严格模式,使用未声明的变量会报错。且必须使用关键词new
3、构造函数new完后还可以new子类,会继承prototype,但class不会
4. JS异步解决方案的发展历程和优缺点
1. 回调函数。回调地狱,不能很好的处理错误。耦合性太强,解决了同步的问题
2. 为了解决callback诞生了promise。链式调用解决了回调函数,缺点无法取消promise,错误需要配合回调函数来补货
3. Generator 可以控制函数的执行
4. Async await 优点代码清晰。不需要写一堆.then,缺点 需要try catch补获错误,并且如果多个请求依赖性不强使用await会将异步变为同步,造成性能的降低
5.实现一个new
function myNew(fn,...args){
const obj = Object.create(fn.prototype)
const res = fn.apply(obj,arg)
return res intanceof Object?res:obj
}
6.判断数据类型及其优缺点
1、typeof 只能判断基本数据类型。判断引用类型时除了function其他都是object,还有null也是object
2、instanceof判断对象是不是某个构造函数的实例。不能判断基本数据类型
手动实现一个
function myInstanceof(data,type){
if(data.proto === type){
return true
while(data){
data= data.__proto
}
}
return false
}
3、使用Object.prototype.tostring.call()
7.简单讲解一下http2的多路复用
为了解决http1,的对头阻塞诞生的。在http1.0的时候每发起请求都会进行一次tcp连接(三次握手四次挥手),后面1.1的时代默认开启了keepalive,同一个链接可以发多个请求,但是也要等服务器接收后才发下一个,也就是说同一时刻只能发一个http请求,这样就会造成一个队头阻塞。在http2中将文本格式传输改为了二进制传输。帧和流,多个帧组成流,多路复用其实就是一个tcp链接可以存在多个流,通过帧中的标识可以知道是哪个流(请求)避免了队头阻塞提高了传输性能。http3的话为了解决tcp丢包(对头阻塞)的问题,基于udp协议的quic协议
8. npm模块的安装机制,为什么输入install会自动安装模块
发出指令会查询nodemodules中是否已存在,若存在不按照,若不存在,npm向registry查询模块压缩包的网址,下载压缩包,解压到nodemodules目录
9. Vuex和redux的区别
都是通过state共享数据,他的状态都是可预测的,在组件内部需要调用特定的方法进行状态变更。单例模式,流程类似。不同点在于vuex他是可变数据,通过mutaion直接对数据进行修改,而redux是通过reduce纯函数,set新值覆盖旧值。发送请求时vuex可以直接action里发送请求,redux则需要借助中间件 redux-thunk
10. 介绍下观察者模式和发布订阅模式的区别,各适用于什么场景
观察者模式中主体和观察者是相互感知的,比如vue的 watcher Dep
而发布订阅模式是借助第三方实现调度的,发布者订阅者之前互不感知
11.讲一下js常见的继承方式
1.原型链继承
将子构造函数的原型指向父构造函数的实例。子构造函数可以访问父构造函数的方法和属性。缺点,改父构造函数会影响子构造函数
function Parent(){} Child.prototype = new Parent()
2.构造函数继承(经典继承)
通过在子构造函数中调用父构造函数实现。子构造函数中通过call和apply方法,将父构造函数的上下文设置为子对象的上下文,实现继承。缺点只继承了实例属性,无法继承原型对象上的方法
function Parent(name){this.name = name} function(name){Parent.call(this,name)}
3.组合继承
结合了原型链继承和构造函数继承,既继承了父构造函数的属性又继承了方法。缺点,调了两次父构造函数,一次是创建原型一次是创建子对象。
4.寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象
funtion objectCopy(obj){
function fun(){}
fun.prototype = obj
return new fun()
}
function create(obj){
const clone = objectCopy(obj)
clone.handler = ()=>{
console.log(1111)
}
return clone
}
5.寄生组合继承 减少组合式继承带来的二次调用带来的性能开销
function inheritPrototype(child,parent){
// 创建一个中间对象 该对象的原型是父类的原型
var prototype = Object.create(parent.prototype)
// 将中间对象的构造函数指向子类
prototype.constructor = child
// 再将子类的原型设置为中间对象
child.prototype = prototype
}
12.cmd amd umd 和CommonJs Esm的区别
cmd是Require.js提出的模块化规范,amd是SeaJs提出的,都是浏览器环境的模块化,通过define定义require引入,强调异步加载,cmd不同处在于他是按需执行的, umd则是一种通用的模块规范,umd可以同时支持amd commonjs
CommonJs主要用于服务端开发,通过module.exports导出,通过require函数加载。CommonJs模块是同步加载的
ESmodule是官方标准模块化,可以进行更好的静态分析和性能,export和import。
13接口安全如何做
使用awes加密,他的库叫crypto.js,服务端使用相同的加密规则
14.webpack
他是一个静态模块打包工具。可以很好的管理模块依赖,编译出模块需要的静态文件
构建过程
1.初始化阶段:从配置文件和命令中进行参数合并,并初始化插件等执行环境所需要的参数
2.编译构建阶段 从entry出发,针对每个module串行调用对应的loader去翻译文件内容,再找到该module所依赖的module递归编译处理
3.输出阶段 对编译后的module进行一个合并,合并成chunk,转换成文件bundle(可放浏览器直接运行)。
loader
loader本质是一个翻译函数。因为webpack只认识js,loader则对img css等进行一个转译处理 常见的有 style-loader css-loader less-loader postcss,url,file-loader,babel,eslint;
plugins
就是插件。可以扩展webpack的功能。他会贯穿webpack的整个编译周期。例如打包优化,资源管理,环境变量注入等。
常用插件
html-webpack-plugin 可以构建多个html和入口 bundle analyse
15.vite
vite是一个基于ES模块的前端构建工具,利用浏览器原生的es模块系统,通过将代码进行拆分,实现了快速冷启动和热模块替换。热模块替换更改配置文件也可以更新。
16.如何理解js的异步
js是一门单线程语言,他运行在浏览器的渲染主线程,而渲染主线程只有一个,他不仅要执行js还要渲染页面,使用异步的方式不进行阻塞。比如计时器,网络请求会交给其他线程去处理。将回调函数包裹成任务加入到消息队列的末尾等待浏览器调度执行。首先是执行
17.Es'6和commonjs模块的相同点和区别
commonjs是运行时加载, Es'6是编译时
commonjs是同步加载多用于服务端,es6是异步加载
commonjs是对模块的浅拷贝,es6只存只读不改变值,指针指向不变
18.说说你对ES6 module
19.使用迭代的方式实现flatten函数
解构
function flatten(arr){
while(arr.some(item=>Array.isArray(item))){
arr = [].concat(...arr)
}
}
递归
function flatten(arr){
const res = []
for(const item of arr){
if(item.constructor ==='Array'){
res.concat(flatten(item))
}else{
res.push(item)
}
}
return res
}
20 为什么Vuex的mutaion和reducer不能做异步操作
因为修改单一数据源state的函数必须是纯函数。统一输入就会统一输出。没有任何副作用。如果是异步则会引入副作用,导致更改后的state不可预测
21.HTTPS握手过程中如何校验证书的合法性
看是否是信任的有效证书,校验是否被吊销,校验证书是否在有效期内,校验私钥是否和证书颁发的一致。
Vue面试题
1.谈谈你对Vue的理解
Vue是用户构建用户界面的渐进式框架。是一个逐一递增的过程(声明式渲染->组件系统->客户端路由->大规模状态管理->构建工具(webpack vite))
1声明式框架更加关注结果而不去关注过程,(非命令式)
2MVVM模式
3.采用虚拟dom
4.区分编译时(打包)和运行时(浏览器)
Vue专门写了个编译时可以将模板编译成虚拟dom(生成render函数),在构建的时候进行模板编译运行的时候不需要
5.组件化 提示开发效率和复用性,降低更新范围,只重新渲染变化的组件
2.谈谈你对SPA 的理解
单页面应用,对应MPA。现在我们用vue和react编写页面都只有一个html页面,并且提供一个挂载点,打包后通过js去动态渲染页面。包括页面切换也是通过前端路由。 单页面与多页面的对比,spa局部刷新,页面切换更快,体验更佳,更容易维护,但是不利于seo优化
3.Vue为什么需要虚拟dom
template会被编译器编译成渲染函数render,挂载时会调用render函数,返回的对象就是虚拟dom。然后在后续的patch过程中进一步生成真实dom 挂载结束后会记录第一次生成的VDOM,当响应式数据发生变化时组件会重新render,生成新的vdom。然后再进行diff操作对比,将更改的部分改到真实dom上从而进行最小量的dom操作更新视图
4.谈谈对Vue组件化的理解
组件化是对ui的封装,模块化是对业务的封装。提升开发效率测试性和复用性,降低了更新范围,只渲染变化的组件
vue中每一个组件都有一个渲染函数watcher,effect(vue3),vue是组件级更新,要合理的拆分组件,否则更新页面都要全页面更新
5.既然Vue可以数据劫持探测到数据变化,为什么还要虚拟dom diff检测差异?
Vue是组件级更新,每个组件都有一个渲染watcher,如果一个属性一个watcher会导致大量watcher的产生浪费内存
6.请你说下对响应式数据的理解
7.Vue中如何检测数组变化
8.Vue中如何进行依赖收集
每个属性都拥有自己的dep属性,存放他所依赖的watcher(通过dep.target保存下来),当属性变化后会通知对应的wathcer更新
Vue3中默认会通过map结果将属性和effect映射起来,初始化时会调用render函数,此时会触发属性依赖收集track,属性发生变化时找到对应的effect列表依次执行trigger
9.Vue.set是如何实现的
vue不允许在已经创建的实例上动态新增新的响应式属性。他会拿到当前的__ob__自身的响应式对象,如果是数组,内部采用了splice来实现。如果是对象修改如果修改 即可。
10.computed和watch区别
Vue2中有三种watcher,渲染watcher,计算属性watcher(computed),用户watcher(watch)
computed内部维护了一个属性dirty为true。初始化时为true,再次取值时dirty为false返回缓存的值。当属性变化时 dirty为true 会触发更新,重新计算新的值。
他的内部计算属性wathcer不会立即执行。lazy为true,内部会通过Object.defineproperty定义成一个属性。当用户取值时会调用get方法,找到对应的wathcer,看dirty决定是否触发运算。同时计算器属性watcher会触发到渲染watcher(watcher.depend()方法)。watch的底层就是new 了一个用户watcher,数据一变化就执行
watch连续输入两个字符时会产生取的上一个值,vue3采用了onCleanup
11.ref和reactive的区别
reactive主要处理对象类型的数据响应式。底层采用 new Proxy() ref通常处理单值的响应式,ref主要解决原始值的响应式问题。底层还是用了Object.definePxxx(),ref会将普通对象包装成一个refimpL对象,如果是对象的话会进行proxy代理,在reactive中使用ref会进行拆包操作。
12.watch和watchEffect区别
watchEffect支持传入一个函数会立即执行,被动的追踪他的依赖,依赖变化重新执行。数据变化后调用scheduler内部会再次触发effect.run()重新执行getter
const effect = new ReactiveEffect(getter,scheduler)
effect.run()
watch侦测数据源变化时会调用回调函数。 scheduler内部调用cb
13.如何将template转换成render函数(编译时完成)
1.将template模版转换成ast语法书-parseHTML
2.遍历语法树找到静态节点,跳过diff操作对静态语法做模板标记,markup diff来做优化的静态节点跳过diff操作
3. 重新生成代码(字符串拼接),用with包裹通过new function的方式产生render函数
14. new Vue的过程发生了什么
1.内部会进行初始化操作init
2.初始化组件的事件,初始化组件的父子关系(children mount挂载
5.内部挂载的时候会产生一个watcher会调用render函数进行依赖收集,内部还会给响应式数据增加dep属性,让属性记录当前的wathcer,vue更新的时候采用虚拟dom的方式进行diff算法更新
15.Vue的声明周期
创建前后,挂载前后,更新前后,卸载前后。服务端没有mounted钩子可以采用creted进行替代。
Vue3中的区别,setup替代了 beforeCreated和created,卸载时改为了beforeUnmount,unmounted
16.Vue中的diff算法原理
内部采用深度递归(比较父后比较子,再比较孙)和双指针的方式进行比较
比较过程
先比较是不是相同节点 key 和tag
相同节点比较属性,并复用老节点(复用真实dom,只改属性)
比较儿子节点 新增改,两个儿子都是列表时(比对差异)后进行更新
优化比较:头头,尾尾,头尾,尾头
17.Vue.use是干什么的
安装Vue插件,如果是一个对象需要抛出install方法,如果是函数直接被作为install方法, 会将Vue作为参数传入,插件中不需要依赖Vue。防止Vue的版本出现冲突。
插件的功能:
1.添加全局指令,过滤器,组件
2.添加Vue实例方法
如何实现
1. Vue.use就是一个函数,通过调用use,执行这个函数。会判断当前插件是否在存在,如果不存在直接执行install方法并且传入参数改变this
18.Vue.extend方法的作用
他是一个Vue构造器函数,参数是组件选项的对象创建一个子类。data是传入一个函数。返回的是一个构造器子类。需要mounted去挂载。内部实现其实是创建一个子类,继承了Vue父类的方法和原型,也会在内部调Vue的初始化。
1.所有的组件都市通过Vue.extend构造出来的
2.可以通过此方法手动挂载组件(new 这个构造器)。在vue3里是render函数,或者telepot。
3.后端存储的字符串模板可以通过Vue.extend进行渲染。
19.函数组件的优势
无状态无生命周期无this,性能比较高。正常创建的组件会通过Vue.extend构造出的子类再去new,并且不会初始化父子组件关系。可以在template中写functional。Vue3中不存在new了,无优势了就。
20 V-once
缓存节点或者组件,只有在初始化时渲染,后续会当成静态节点提高性能。缺点是如果数据需要变化,不会重新渲染。 Vue3中增加了V-memo,指定某些属性可以数据变化重新渲染
21 Vue.mixin的使用场景
mixin可以扩展组件,将公共逻辑抽离。在需要该逻辑的时候进行混入。内部实现是采用策略模式针对不同的属性进行合并。合并策略。缺点命名冲突和数据来源问题。Vue3使用compositionAPI提取公共逻辑。
22.hash和history的区别
hash不会向浏览器发请求。history会
hash兼容性更强,可以在本地文件环境中运行
Ts相关
1.类型批注
2.类型推断
3.数据类型
boolean number string array,tuple元祖 enum枚举 any,null.void,undefined,never,object
元祖:已知数量,已知类型
let turpleArr :[number,string,boolean]
null和undefined是其他类的子类,赋值给string,number类型
never是任何类型的子类型代表不会出现的值。最底层
void 空类型,方法没有返回值
4.谈谈你对ts中高级类型的理解
1.交叉类型
T&U 同属于两种类型
interface parent{name:stirng}
interface child{age:number}
type Person = parent & child
const name:Person = {name:'测试',age:2}
2.联合类型
或。多个类型中的任意一个。 T|U
3.类型别名
type some = xxx
type some = boolean | string
const a:some = true
4.类型索引
keyof 类似Object.keys
interface Button {
type:string
text:string
}
type ButtonKeys = keyof Button 输出key的联合类型 type|text. 多用于遍历
5.类型约束
extends
type BaseType = string | number |boolean
function copy<T extends BaseType>(args:T):T{
return arg
}
类型约束,类型索引一起用
k必须是T的索引才可以返回正确值
function getValue<T,K extends keyof T>(obj:T,key:K){
return obj[key]
}
6. 类型映射
对所有类型变成只读
type Readonly<T>={
readonly [p in keyof T]:T[P]
}
interface Obj{
name:string
age:string
}
type ReadOblyObj = Readonly<obj> 会将Obj变为only的属性
7.条件类型
T extends U ? X:Y T是U的子集,使用X否则使用Y
Ts的高阶类型可以覆盖更多的场景。
5.说说你对接口的理解,应用场景
方法特征的集合。interface
interface IUser{
name:string
age:number
}
可以给调用方知道传参和出参。
const getUserInfo = (user:IUser):string=>{
return `name:${user.name},age:${user.age}`
}
6.ts中的类
类是一种用户定义的引用类型。ts中全面支持了面向对象编程的内容。比es6多了 类 接口 class
1.字段:定义类中的变量
2.构造函数:类实例化时调用,为类的对象分配内存
3.方法:对象中要执行的操作
class Car{
engine:string;
constructor(enigine:string){
this.enigine = enigine
}
post():void{
console.log('')
}
}
7.什么是ts
ts是js的超集
支持ES6的语法
支持面向对象编程的概念。类 接口 继承 泛型
ts不支持在浏览器中运行,需要编译成js
8.为什么要用ts,他的优势有哪些
增加了静态类型。代码质量更好。
优势:类型一定程度上充当文档。不需要翻源码,编辑器自动填充,自动联想。
9.const readonly的区别
const防止变量值被修改,readonly 防止变量属性被更改
接口和类型别名都可以描述对象或函数类型。类型别名可以用于其他类型,基本类型,联合类型 ,元组
10.any never unknow null undefined void的区别
any 动态类型变量,失去了类型检查的作用
never 永远不存在的值的类型。(抛出异常,不会有返回值的箭头函数)
unknow 任何类型的值都可以赋值给 unknow。
null undefined 默认是所有类型的子类型。可以赋值给其他类型(如number的变量)。如果开启了strictNullCheck只能赋值给自身或void。
void 没有任何类型
11 ts中用interface给Function Array Class做声明
interface Say{
(name:string):void
}
let say:Say = (name:string):void=>{}
interface NumberArray{
[index:number]:number
}
const list:NumberArray = [1,2,3]
interface Person{
name:string
sayHi(name:string):string
}
12.Ts中Union Types(联合类型)需要注意哪些
只能访问共同的属性和方法,否则会报错
13.ts中如何使用联合类型的key
enum str{A,B,V}
type strUnion = keyof typeof str; 'A'|'B'|'C'
14.ts中type和interface的区别
相同点:都可以描述对象和函数,都允许拓展
不同点:
type类型别名可以声明基本类型,联合类型 元组,interface只能声明对象类型
type 可以使用typeof获取实例类型进行赋值
多个interface可以自动合并。,type不行。
interface可以通过extends实现继承,type需要使用&符
15.简单聊聊你对ts类型兼容性的理解
类型兼容,相同结构会被认为是同一类型。接口兼容,X=Y Y中的成员必须更多
16.Ts中对象展开会有什么副作用
后面的属性会覆盖前面的属性,仅包含可枚举的属性。不可枚举的属性会丢失。
17.declare,decclare global是什么
declare 定义全局变量,全局函数,全局命名空间 js modules class等
declare global为全局对象, window增加新的属性
18.ts类中public private,protected,readonly修饰符的理解
private 私有 类内部访问
protected 类内部以及类子类访问
19. keyof和typeof关键词的作用
keyof对索引类型查询操作符,获取索引类型的属性名,来构成联合类型